Full source code website bán hàng thương mại điện tử gần giống shopee
467.443 lượt xem;
- class.phpmailer.php
- project /
1 <?php
2 /**
3 * PHPMailer - PHP email creation and transport class.
4 * PHP Version 5
5 * @package PHPMailer
6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10 * @author Brent R. Matzelle (original founder)
11 * @copyright 2012 - 2014 Marcus Bointon
12 * @copyright 2010 - 2012 Jim Jagielski
13 * @copyright 2004 - 2009 Andy Prevost
14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15 * @note This program is distributed in the hope that it will be useful - WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 /**
21 * PHPMailer - PHP email creation and transport class.
22 * @package PHPMailer
23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
24 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
25 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
26 * @author Brent R. Matzelle (original founder)
27 */
28 class PHPMailer
29 {
30 /**
31 * The PHPMailer Version number.
32 * @type string
33 */
34 public $Version = '5.2.9';
35
36 /**
37 * Email priority.
38 * Options: 1 = High, 3 = Normal, 5 = low.
39 * @type integer
40 */
41 public $Priority = 3;
42
43 /**
44 * The character set of the message.
45 * @type string
46 */
47 public $CharSet = 'iso-8859-1';
48
49 /**
50 * The MIME Content-type of the message.
51 * @type string
52 */
53 public $ContentType = 'text/plain';
54
55 /**
56 * The message encoding.
57 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
58 * @type string
59 */
60 public $Encoding = '8bit';
61
62 /**
63 * Holds the most recent mailer error message.
64 * @type string
65 */
66 public $ErrorInfo = '';
67
68 /**
69 * The From email address for the message.
70 * @type string
71 */
72 public $From = 'root@localhost';
73
74 /**
75 * The From name of the message.
76 * @type string
77 */
78 public $FromName = 'Root User';
79
80 /**
81 * The Sender email (Return-Path) of the message.
82 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
83 * @type string
84 */
85 public $Sender = '';
86
87 /**
88 * The Return-Path of the message.
89 * If empty, it will be set to either From or Sender.
90 * @type string
91 * @deprecated Email senders should never set a return-path header;
92 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
93 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
94 */
95 public $ReturnPath = '';
96
97 /**
98 * The Subject of the message.
99 * @type string
100 */
101 public $Subject = '';
102
103 /**
104 * An HTML or plain text message body.
105 * If HTML then call isHTML(true).
106 * @type string
107 */
108 public $Body = '';
109
110 /**
111 * The plain-text message body.
112 * This body can be read by mail clients that do not have HTML email
113 * capability such as mutt & Eudora.
114 * Clients that can read HTML will view the normal Body.
115 * @type string
116 */
117 public $AltBody = '';
118
119 /**
120 * An iCal message part body.
121 * Only supported in simple alt or alt_inline message types
122 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
123 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
124 * @link http://kigkonsult.se/iCalcreator/
125 * @type string
126 */
127 public $Ical = '';
128
129 /**
130 * The complete compiled MIME message body.
131 * @access protected
132 * @type string
133 */
134 protected $MIMEBody = '';
135
136 /**
137 * The complete compiled MIME message headers.
138 * @type string
139 * @access protected
140 */
141 protected $MIMEHeader = '';
142
143 /**
144 * Extra headers that createHeader() doesn't fold in.
145 * @type string
146 * @access protected
147 */
148 protected $mailHeader = '';
149
150 /**
151 * Word-wrap the message body to this number of chars.
152 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
153 * @type integer
154 */
155 public $WordWrap = 0;
156
157 /**
158 * Which method to use to send mail.
159 * Options: "mail", "sendmail", or "smtp".
160 * @type string
161 */
162 public $Mailer = 'mail';
163
164 /**
165 * The path to the sendmail program.
166 * @type string
167 */
168 public $Sendmail = '/usr/sbin/sendmail';
169
170 /**
171 * Whether mail() uses a fully sendmail-compatible MTA.
172 * One which supports sendmail's "-oi -f" options.
173 * @type boolean
174 */
175 public $UseSendmailOptions = true;
176
177 /**
178 * Path to PHPMailer plugins.
179 * Useful if the SMTP class is not in the PHP include path.
180 * @type string
181 * @deprecated Should not be needed now there is an autoloader.
182 */
183 public $PluginDir = '';
184
185 /**
186 * The email address that a reading confirmation should be sent to.
187 * @type string
188 */
189 public $ConfirmReadingTo = '';
190
191 /**
192 * The hostname to use in Message-Id and Received headers
193 * and as default HELO string.
194 * If empty, the value returned
195 * by SERVER_NAME is used or 'localhost.localdomain'.
196 * @type string
197 */
198 public $Hostname = '';
199
200 /**
201 * An ID to be used in the Message-Id header.
202 * If empty, a unique id will be generated.
203 * @type string
204 */
205 public $MessageID = '';
206
207 /**
208 * The message Date to be used in the Date header.
209 * If empty, the current date will be added.
210 * @type string
211 */
212 public $MessageDate = '';
213
214 /**
215 * SMTP hosts.
216 * Either a single hostname or multiple semicolon-delimited hostnames.
217 * You can also specify a different port
218 * for each host by using this format: [hostname:port]
219 * (e.g. "smtp1.example.com:25;smtp2.example.com").
220 * You can also specify encryption type, for example:
221 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
222 * Hosts will be tried in order.
223 * @type string
224 */
225 public $Host = 'localhost';
226
227 /**
228 * The default SMTP server port.
229 * @type integer
230 * @TODO Why is this needed when the SMTP class takes care of it?
231 */
232 public $Port = 25;
233
234 /**
235 * The SMTP HELO of the message.
236 * Default is $Hostname.
237 * @type string
238 * @see PHPMailer::$Hostname
239 */
240 public $Helo = '';
241
242 /**
243 * The secure connection prefix.
244 * Options: "", "ssl" or "tls"
245 * @type string
246 */
247 public $SMTPSecure = '';
248
249 /**
250 * Whether to use SMTP authentication.
251 * Uses the Username and Password properties.
252 * @type boolean
253 * @see PHPMailer::$Username
254 * @see PHPMailer::$Password
255 */
256 public $SMTPAuth = false;
257
258 /**
259 * SMTP username.
260 * @type string
261 */
262 public $Username = '';
263
264 /**
265 * SMTP password.
266 * @type string
267 */
268 public $Password = '';
269
270 /**
271 * SMTP auth type.
272 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
273 * @type string
274 */
275 public $AuthType = '';
276
277 /**
278 * SMTP realm.
279 * Used for NTLM auth
280 * @type string
281 */
282 public $Realm = '';
283
284 /**
285 * SMTP workstation.
286 * Used for NTLM auth
287 * @type string
288 */
289 public $Workstation = '';
290
291 /**
292 * The SMTP server timeout in seconds.
293 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
294 * @type integer
295 */
296 public $Timeout = 300;
297
298 /**
299 * SMTP class debug output mode.
300 * Debug output level.
301 * Options:
302 * * `0` No output
303 * * `1` Commands
304 * * `2` Data and commands
305 * * `3` As 2 plus connection status
306 * * `4` Low-level data output
307 * @type integer
308 * @see SMTP::$do_debug
309 */
310 public $SMTPDebug = 0;
311
312 /**
313 * How to handle debug output.
314 * Options:
315 * * `echo` Output plain-text as-is, appropriate for CLI
316 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
317 * * `error_log` Output to error log as configured in php.ini
318 *
319 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
320 * <code>
321 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
322 * </code>
323 * @type string|callable
324 * @see SMTP::$Debugoutput
325 */
326 public $Debugoutput = 'echo';
327
328 /**
329 * Whether to keep SMTP connection open after each message.
330 * If this is set to true then to close the connection
331 * requires an explicit call to smtpClose().
332 * @type boolean
333 */
334 public $SMTPKeepAlive = false;
335
336 /**
337 * Whether to split multiple to addresses into multiple messages
338 * or send them all in one message.
339 * @type boolean
340 */
341 public $SingleTo = false;
342
343 /**
344 * Storage for addresses when SingleTo is enabled.
345 * @type array
346 * @TODO This should really not be public
347 */
348 public $SingleToArray = array();
349
350 /**
351 * Whether to generate VERP addresses on send.
352 * Only applicable when sending via SMTP.
353 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
354 * @link http://www.postfix.org/VERP_README.html Postfix VERP info
355 * @type boolean
356 */
357 public $do_verp = false;
358
359 /**
360 * Whether to allow sending messages with an empty body.
361 * @type boolean
362 */
363 public $AllowEmpty = false;
364
365 /**
366 * The default line ending.
367 * @note The default remains "\n". We force CRLF where we know
368 * it must be used via self::CRLF.
369 * @type string
370 */
371 public $LE = "\n";
372
373 /**
374 * DKIM selector.
375 * @type string
376 */
377 public $DKIM_selector = '';
378
379 /**
380 * DKIM Identity.
381 * Usually the email address used as the source of the email
382 * @type string
383 */
384 public $DKIM_identity = '';
385
386 /**
387 * DKIM passphrase.
388 * Used if your key is encrypted.
389 * @type string
390 */
391 public $DKIM_passphrase = '';
392
393 /**
394 * DKIM signing domain name.
395 * @example 'example.com'
396 * @type string
397 */
398 public $DKIM_domain = '';
399
400 /**
401 * DKIM private key file path.
402 * @type string
403 */
404 public $DKIM_private = '';
405
406 /**
407 * Callback Action function name.
408 *
409 * The function that handles the result of the send email action.
410 * It is called out by send() for each email sent.
411 *
412 * Value can be any php callable: http://www.php.net/is_callable
413 *
414 * Parameters:
415 * boolean $result result of the send action
416 * string $to email address of the recipient
417 * string $cc cc email addresses
418 * string $bcc bcc email addresses
419 * string $subject the subject
420 * string $body the email body
421 * string $from email address of sender
422 * @type string
423 */
424 public $action_function = '';
425
426 /**
427 * What to put in the X-Mailer header.
428 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
429 * @type string
430 */
431 public $XMailer = '';
432
433 /**
434 * An instance of the SMTP sender class.
435 * @type SMTP
436 * @access protected
437 */
438 protected $smtp = null;
439
440 /**
441 * The array of 'to' addresses.
442 * @type array
443 * @access protected
444 */
445 protected $to = array();
446
447 /**
448 * The array of 'cc' addresses.
449 * @type array
450 * @access protected
451 */
452 protected $cc = array();
453
454 /**
455 * The array of 'bcc' addresses.
456 * @type array
457 * @access protected
458 */
459 protected $bcc = array();
460
461 /**
462 * The array of reply-to names and addresses.
463 * @type array
464 * @access protected
465 */
466 protected $ReplyTo = array();
467
468 /**
469 * An array of all kinds of addresses.
470 * Includes all of $to, $cc, $bcc, $replyto
471 * @type array
472 * @access protected
473 */
474 protected $all_recipients = array();
475
476 /**
477 * The array of attachments.
478 * @type array
479 * @access protected
480 */
481 protected $attachment = array();
482
483 /**
484 * The array of custom headers.
485 * @type array
486 * @access protected
487 */
488 protected $CustomHeader = array();
489
490 /**
491 * The most recent Message-ID (including angular brackets).
492 * @type string
493 * @access protected
494 */
495 protected $lastMessageID = '';
496
497 /**
498 * The message's MIME type.
499 * @type string
500 * @access protected
501 */
502 protected $message_type = '';
503
504 /**
505 * The array of MIME boundary strings.
506 * @type array
507 * @access protected
508 */
509 protected $boundary = array();
510
511 /**
512 * The array of available languages.
513 * @type array
514 * @access protected
515 */
516 protected $language = array();
517
518 /**
519 * The number of errors encountered.
520 * @type integer
521 * @access protected
522 */
523 protected $error_count = 0;
524
525 /**
526 * The S/MIME certificate file path.
527 * @type string
528 * @access protected
529 */
530 protected $sign_cert_file = '';
531
532 /**
533 * The S/MIME key file path.
534 * @type string
535 * @access protected
536 */
537 protected $sign_key_file = '';
538
539 /**
540 * The S/MIME password for the key.
541 * Used only if the key is encrypted.
542 * @type string
543 * @access protected
544 */
545 protected $sign_key_pass = '';
546
547 /**
548 * Whether to throw exceptions for errors.
549 * @type boolean
550 * @access protected
551 */
552 protected $exceptions = false;
553
554 /**
555 * Error severity: message only, continue processing.
556 */
557 const STOP_MESSAGE = 0;
558
559 /**
560 * Error severity: message, likely ok to continue processing.
561 */
562 const STOP_CONTINUE = 1;
563
564 /**
565 * Error severity: message, plus full stop, critical error reached.
566 */
567 const STOP_CRITICAL = 2;
568
569 /**
570 * SMTP RFC standard line ending.
571 */
572 const CRLF = "\r\n";
573
574 /**
575 * Constructor.
576 * @param boolean $exceptions Should we throw external exceptions?
577 */
578 public function __construct($exceptions = false)
579 {
580 $this->exceptions = (boolean)$exceptions;
581 }
582
583 /**
584 * Destructor.
585 */
586 public function __destruct()
587 {
588 if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
589 $this->smtpClose();
590 }
591 }
592
593 /**
594 * Call mail() in a safe_mode-aware fashion.
595 * Also, unless sendmail_path points to sendmail (or something that
596 * claims to be sendmail), don't pass params (not a perfect fix,
597 * but it will do)
598 * @param string $to To
599 * @param string $subject Subject
600 * @param string $body Message Body
601 * @param string $header Additional Header(s)
602 * @param string $params Params
603 * @access private
604 * @return boolean
605 */
606 private function mailPassthru($to, $subject, $body, $header, $params)
607 {
608 //Check overloading of mail function to avoid double-encoding
609 if (ini_get('mbstring.func_overload') & 1) {
610 $subject = $this->secureHeader($subject);
611 } else {
612 $subject = $this->encodeHeader($this->secureHeader($subject));
613 }
614 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
615 $result = @mail($to, $subject, $body, $header);
616 } else {
617 $result = @mail($to, $subject, $body, $header, $params);
618 }
619 return $result;
620 }
621
622 /**
623 * Output debugging info via user-defined method.
624 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
625 * @see PHPMailer::$Debugoutput
626 * @see PHPMailer::$SMTPDebug
627 * @param string $str
628 */
629 protected function edebug($str)
630 {
631 if ($this->SMTPDebug <= 0) {
632 return;
633 }
634 //Avoid clash with built-in function names
635 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
636 call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
637 return;
638 }
639 switch ($this->Debugoutput) {
640 case 'error_log':
641 //Don't output, just log
642 error_log($str);
643 break;
644 case 'html':
645 //Cleans up output a bit for a better looking, HTML-safe output
646 echo htmlentities(
647 preg_replace('/[\r\n]+/', '', $str),
648 ENT_QUOTES,
649 'UTF-8'
650 )
651 . "<br>\n";
652 break;
653 case 'echo':
654 default:
655 //Normalize line breaks
656 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
657 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
658 "\n",
659 "\n \t ",
660 trim($str)
661 ) . "\n";
662 }
663 }
664
665 /**
666 * Sets message type to HTML or plain.
667 * @param boolean $isHtml True for HTML mode.
668 * @return void
669 */
670 public function isHTML($isHtml = true)
671 {
672 if ($isHtml) {
673 $this->ContentType = 'text/html';
674 } else {
675 $this->ContentType = 'text/plain';
676 }
677 }
678
679 /**
680 * Send messages using SMTP.
681 * @return void
682 */
683 public function isSMTP()
684 {
685 $this->Mailer = 'smtp';
686 }
687
688 /**
689 * Send messages using PHP's mail() function.
690 * @return void
691 */
692 public function isMail()
693 {
694 $this->Mailer = 'mail';
695 }
696
697 /**
698 * Send messages using $Sendmail.
699 * @return void
700 */
701 public function isSendmail()
702 {
703 $ini_sendmail_path = ini_get('sendmail_path');
704
705 if (!stristr($ini_sendmail_path, 'sendmail')) {
706 $this->Sendmail = '/usr/sbin/sendmail';
707 } else {
708 $this->Sendmail = $ini_sendmail_path;
709 }
710 $this->Mailer = 'sendmail';
711 }
712
713 /**
714 * Send messages using qmail.
715 * @return void
716 */
717 public function isQmail()
718 {
719 $ini_sendmail_path = ini_get('sendmail_path');
720
721 if (!stristr($ini_sendmail_path, 'qmail')) {
722 $this->Sendmail = '/var/qmail/bin/qmail-inject';
723 } else {
724 $this->Sendmail = $ini_sendmail_path;
725 }
726 $this->Mailer = 'qmail';
727 }
728
729 /**
730 * Add a "To" address.
731 * @param string $address
732 * @param string $name
733 * @return boolean true on success, false if address already used
734 */
735 public function addAddress($address, $name = '')
736 {
737 return $this->addAnAddress('to', $address, $name);
738 }
739
740 /**
741 * Add a "CC" address.
742 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
743 * @param string $address
744 * @param string $name
745 * @return boolean true on success, false if address already used
746 */
747 public function addCC($address, $name = '')
748 {
749 return $this->addAnAddress('cc', $address, $name);
750 }
751
752 /**
753 * Add a "BCC" address.
754 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
755 * @param string $address
756 * @param string $name
757 * @return boolean true on success, false if address already used
758 */
759 public function addBCC($address, $name = '')
760 {
761 return $this->addAnAddress('bcc', $address, $name);
762 }
763
764 /**
765 * Add a "Reply-to" address.
766 * @param string $address
767 * @param string $name
768 * @return boolean
769 */
770 public function addReplyTo($address, $name = '')
771 {
772 return $this->addAnAddress('Reply-To', $address, $name);
773 }
774
775 /**
776 * Add an address to one of the recipient arrays.
777 * Addresses that have been added already return false, but do not throw exceptions
778 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
779 * @param string $address The email address to send to
780 * @param string $name
781 * @throws phpmailerException
782 * @return boolean true on success, false if address already used or invalid in some way
783 * @access protected
784 */
785 protected function addAnAddress($kind, $address, $name = '')
786 {
787 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
788 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
789 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
790 if ($this->exceptions) {
791 throw new phpmailerException('Invalid recipient array: ' . $kind);
792 }
793 return false;
794 }
795 $address = trim($address);
796 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
797 if (!$this->validateAddress($address)) {
798 $this->setError($this->lang('invalid_address') . ': ' . $address);
799 $this->edebug($this->lang('invalid_address') . ': ' . $address);
800 if ($this->exceptions) {
801 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
802 }
803 return false;
804 }
805 if ($kind != 'Reply-To') {
806 if (!isset($this->all_recipients[strtolower($address)])) {
807 array_push($this->$kind, array($address, $name));
808 $this->all_recipients[strtolower($address)] = true;
809 return true;
810 }
811 } else {
812 if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
813 $this->ReplyTo[strtolower($address)] = array($address, $name);
814 return true;
815 }
816 }
817 return false;
818 }
819
820 /**
821 * Set the From and FromName properties.
822 * @param string $address
823 * @param string $name
824 * @param boolean $auto Whether to also set the Sender address, defaults to true
825 * @throws phpmailerException
826 * @return boolean
827 */
828 public function setFrom($address, $name = '', $auto = true)
829 {
830 $address = trim($address);
831 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
832 if (!$this->validateAddress($address)) {
833 $this->setError($this->lang('invalid_address') . ': ' . $address);
834 $this->edebug($this->lang('invalid_address') . ': ' . $address);
835 if ($this->exceptions) {
836 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
837 }
838 return false;
839 }
840 $this->From = $address;
841 $this->FromName = $name;
842 if ($auto) {
843 if (empty($this->Sender)) {
844 $this->Sender = $address;
845 }
846 }
847 return true;
848 }
849
850 /**
851 * Return the Message-ID header of the last email.
852 * Technically this is the value from the last time the headers were created,
853 * but it's also the message ID of the last sent message except in
854 * pathological cases.
855 * @return string
856 */
857 public function getLastMessageID()
858 {
859 return $this->lastMessageID;
860 }
861
862 /**
863 * Check that a string looks like an email address.
864 * @param string $address The email address to check
865 * @param string $patternselect A selector for the validation pattern to use :
866 * * `auto` Pick strictest one automatically;
867 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
868 * * `pcre` Use old PCRE implementation;
869 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
870 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
871 * * `noregex` Don't use a regex: super fast, really dumb.
872 * @return boolean
873 * @static
874 * @access public
875 */
876 public static function validateAddress($address, $patternselect = 'auto')
877 {
878 if (!$patternselect or $patternselect == 'auto') {
879 //Check this constant first so it works when extension_loaded() is disabled by safe mode
880 //Constant was added in PHP 5.2.4
881 if (defined('PCRE_VERSION')) {
882 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
883 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
884 $patternselect = 'pcre8';
885 } else {
886 $patternselect = 'pcre';
887 }
888 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
889 //Fall back to older PCRE
890 $patternselect = 'pcre';
891 } else {
892 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
893 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
894 $patternselect = 'php';
895 } else {
896 $patternselect = 'noregex';
897 }
898 }
899 }
900 switch ($patternselect) {
901 case 'pcre8':
902 /**
903 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
904 * @link http://squiloople.com/2009/12/20/email-address-validation/
905 * @copyright 2009-2010 Michael Rushton
906 * Feel free to use and redistribute this code. But please keep this copyright notice.
907 */
908 return (boolean)preg_match(
909 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
910 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
911 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
912 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
913 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
914 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
915 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
916 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
917 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
918 $address
919 );
920 case 'pcre':
921 //An older regex that doesn't need a recent PCRE
922 return (boolean)preg_match(
923 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
924 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
925 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
926 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
927 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
928 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
929 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
930 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
931 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
932 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
933 $address
934 );
935 case 'html5':
936 /**
937 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
938 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
939 */
940 return (boolean)preg_match(
941 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
942 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
943 $address
944 );
945 case 'noregex':
946 //No PCRE! Do something _very_ approximate!
947 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
948 return (strlen($address) >= 3
949 and strpos($address, '@') >= 1
950 and strpos($address, '@') != strlen($address) - 1);
951 case 'php':
952 default:
953 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
954 }
955 }
956
957 /**
958 * Create a message and send it.
959 * Uses the sending method specified by $Mailer.
960 * @throws phpmailerException
961 * @return boolean false on error - See the ErrorInfo property for details of the error.
962 */
963 public function send()
964 {
965 try {
966 if (!$this->preSend()) {
967 return false;
968 }
969 return $this->postSend();
970 } catch (phpmailerException $exc) {
971 $this->mailHeader = '';
972 $this->setError($exc->getMessage());
973 if ($this->exceptions) {
974 throw $exc;
975 }
976 return false;
977 }
978 }
979
980 /**
981 * Prepare a message for sending.
982 * @throws phpmailerException
983 * @return boolean
984 */
985 public function preSend()
986 {
987 try {
988 $this->mailHeader = '';
989 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
990 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
991 }
992
993 // Set whether the message is multipart/alternative
994 if (!empty($this->AltBody)) {
995 $this->ContentType = 'multipart/alternative';
996 }
997
998 $this->error_count = 0; // reset errors
999 $this->setMessageType();
1000 // Refuse to send an empty message unless we are specifically allowing it
1001 if (!$this->AllowEmpty and empty($this->Body)) {
1002 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1003 }
1004
1005 $this->MIMEHeader = $this->createHeader();
1006 $this->MIMEBody = $this->createBody();
1007
1008 // To capture the complete message when using mail(), create
1009 // an extra header list which createHeader() doesn't fold in
1010 if ($this->Mailer == 'mail') {
1011 if (count($this->to) > 0) {
1012 $this->mailHeader .= $this->addrAppend('To', $this->to);
1013 } else {
1014 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1015 }
1016 $this->mailHeader .= $this->headerLine(
1017 'Subject',
1018 $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1019 );
1020 }
1021
1022 // Sign with DKIM if enabled
1023 if (!empty($this->DKIM_domain)
1024 && !empty($this->DKIM_private)
1025 && !empty($this->DKIM_selector)
1026 && file_exists($this->DKIM_private)) {
1027 $header_dkim = $this->DKIM_Add(
1028 $this->MIMEHeader . $this->mailHeader,
1029 $this->encodeHeader($this->secureHeader($this->Subject)),
1030 $this->MIMEBody
1031 );
1032 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1033 str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1034 }
1035 return true;
1036
1037 } catch (phpmailerException $exc) {
1038 $this->setError($exc->getMessage());
1039 if ($this->exceptions) {
1040 throw $exc;
1041 }
1042 return false;
1043 }
1044 }
1045
1046 /**
1047 * Actually send a message.
1048 * Send the email via the selected mechanism
1049 * @throws phpmailerException
1050 * @return boolean
1051 */
1052 public function postSend()
1053 {
1054 try {
1055 // Choose the mailer and send through it
1056 switch ($this->Mailer) {
1057 case 'sendmail':
1058 case 'qmail':
1059 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1060 case 'smtp':
1061 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1062 case 'mail':
1063 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1064 default:
1065 $sendMethod = $this->Mailer.'Send';
1066 if (method_exists($this, $sendMethod)) {
1067 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1068 }
1069
1070 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1071 }
1072 } catch (phpmailerException $exc) {
1073 $this->setError($exc->getMessage());
1074 $this->edebug($exc->getMessage());
1075 if ($this->exceptions) {
1076 throw $exc;
1077 }
1078 }
1079 return false;
1080 }
1081
1082 /**
1083 * Send mail using the $Sendmail program.
1084 * @param string $header The message headers
1085 * @param string $body The message body
1086 * @see PHPMailer::$Sendmail
1087 * @throws phpmailerException
1088 * @access protected
1089 * @return boolean
1090 */
1091 protected function sendmailSend($header, $body)
1092 {
1093 if ($this->Sender != '') {
1094 if ($this->Mailer == 'qmail') {
1095 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1096 } else {
1097 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1098 }
1099 } else {
1100 if ($this->Mailer == 'qmail') {
1101 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1102 } else {
1103 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1104 }
1105 }
1106 if ($this->SingleTo) {
1107 foreach ($this->SingleToArray as $toAddr) {
1108 if (!@$mail = popen($sendmail, 'w')) {
1109 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1110 }
1111 fputs($mail, 'To: ' . $toAddr . "\n");
1112 fputs($mail, $header);
1113 fputs($mail, $body);
1114 $result = pclose($mail);
1115 $this->doCallback(
1116 ($result == 0),
1117 array($toAddr),
1118 $this->cc,
1119 $this->bcc,
1120 $this->Subject,
1121 $body,
1122 $this->From
1123 );
1124 if ($result != 0) {
1125 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1126 }
1127 }
1128 } else {
1129 if (!@$mail = popen($sendmail, 'w')) {
1130 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1131 }
1132 fputs($mail, $header);
1133 fputs($mail, $body);
1134 $result = pclose($mail);
1135 $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1136 if ($result != 0) {
1137 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1138 }
1139 }
1140 return true;
1141 }
1142
1143 /**
1144 * Send mail using the PHP mail() function.
1145 * @param string $header The message headers
1146 * @param string $body The message body
1147 * @link http://www.php.net/manual/en/book.mail.php
1148 * @throws phpmailerException
1149 * @access protected
1150 * @return boolean
1151 */
1152 protected function mailSend($header, $body)
1153 {
1154 $toArr = array();
1155 foreach ($this->to as $toaddr) {
1156 $toArr[] = $this->addrFormat($toaddr);
1157 }
1158 $to = implode(', ', $toArr);
1159
1160 if (empty($this->Sender)) {
1161 $params = ' ';
1162 } else {
1163 $params = sprintf('-f%s', $this->Sender);
1164 }
1165 if ($this->Sender != '' and !ini_get('safe_mode')) {
1166 $old_from = ini_get('sendmail_from');
1167 ini_set('sendmail_from', $this->Sender);
1168 }
1169 $result = false;
1170 if ($this->SingleTo && count($toArr) > 1) {
1171 foreach ($toArr as $toAddr) {
1172 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1173 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1174 }
1175 } else {
1176 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1177 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1178 }
1179 if (isset($old_from)) {
1180 ini_set('sendmail_from', $old_from);
1181 }
1182 if (!$result) {
1183 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1184 }
1185 return true;
1186 }
1187
1188 /**
1189 * Get an instance to use for SMTP operations.
1190 * Override this function to load your own SMTP implementation
1191 * @return SMTP
1192 */
1193 public function getSMTPInstance()
1194 {
1195 if (!is_object($this->smtp)) {
1196 $this->smtp = new SMTP;
1197 }
1198 return $this->smtp;
1199 }
1200
1201 /**
1202 * Send mail via SMTP.
1203 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1204 * Uses the PHPMailerSMTP class by default.
1205 * @see PHPMailer::getSMTPInstance() to use a different class.
1206 * @param string $header The message headers
1207 * @param string $body The message body
1208 * @throws phpmailerException
1209 * @uses SMTP
1210 * @access protected
1211 * @return boolean
1212 */
1213 protected function smtpSend($header, $body)
1214 {
1215 $bad_rcpt = array();
1216
1217 if (!$this->smtpConnect()) {
1218 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1219 }
1220 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1221 if (!$this->smtp->mail($smtp_from)) {
1222 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1223 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1224 }
1225
1226 // Attempt to send to all recipients
1227 foreach ($this->to as $to) {
1228 if (!$this->smtp->recipient($to[0])) {
1229 $bad_rcpt[] = $to[0];
1230 $isSent = false;
1231 } else {
1232 $isSent = true;
1233 }
1234 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1235 }
1236 foreach ($this->cc as $cc) {
1237 if (!$this->smtp->recipient($cc[0])) {
1238 $bad_rcpt[] = $cc[0];
1239 $isSent = false;
1240 } else {
1241 $isSent = true;
1242 }
1243 $this->doCallback($isSent, array(), array($cc[0]), array(), $this->Subject, $body, $this->From);
1244 }
1245 foreach ($this->bcc as $bcc) {
1246 if (!$this->smtp->recipient($bcc[0])) {
1247 $bad_rcpt[] = $bcc[0];
1248 $isSent = false;
1249 } else {
1250 $isSent = true;
1251 }
1252 $this->doCallback($isSent, array(), array(), array($bcc[0]), $this->Subject, $body, $this->From);
1253 }
1254
1255 // Only send the DATA command if we have viable recipients
1256 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1257 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1258 }
1259 if ($this->SMTPKeepAlive) {
1260 $this->smtp->reset();
1261 } else {
1262 $this->smtp->quit();
1263 $this->smtp->close();
1264 }
1265 if (count($bad_rcpt) > 0) { // Create error message for any bad addresses
1266 throw new phpmailerException(
1267 $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
1268 self::STOP_CONTINUE
1269 );
1270 }
1271 return true;
1272 }
1273
1274 /**
1275 * Initiate a connection to an SMTP server.
1276 * Returns false if the operation failed.
1277 * @param array $options An array of options compatible with stream_context_create()
1278 * @uses SMTP
1279 * @access public
1280 * @throws phpmailerException
1281 * @return boolean
1282 */
1283 public function smtpConnect($options = array())
1284 {
1285 if (is_null($this->smtp)) {
1286 $this->smtp = $this->getSMTPInstance();
1287 }
1288
1289 // Already connected?
1290 if ($this->smtp->connected()) {
1291 return true;
1292 }
1293
1294 $this->smtp->setTimeout($this->Timeout);
1295 $this->smtp->setDebugLevel($this->SMTPDebug);
1296 $this->smtp->setDebugOutput($this->Debugoutput);
1297 $this->smtp->setVerp($this->do_verp);
1298 $hosts = explode(';', $this->Host);
1299 $lastexception = null;
1300
1301 foreach ($hosts as $hostentry) {
1302 $hostinfo = array();
1303 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1304 // Not a valid host entry
1305 continue;
1306 }
1307 // $hostinfo[2]: optional ssl or tls prefix
1308 // $hostinfo[3]: the hostname
1309 // $hostinfo[4]: optional port number
1310 // The host string prefix can temporarily override the current setting for SMTPSecure
1311 // If it's not specified, the default value is used
1312 $prefix = '';
1313 $tls = ($this->SMTPSecure == 'tls');
1314 if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
1315 $prefix = 'ssl://';
1316 $tls = false; // Can't have SSL and TLS at once
1317 } elseif ($hostinfo[2] == 'tls') {
1318 $tls = true;
1319 // tls doesn't use a prefix
1320 }
1321 $host = $hostinfo[3];
1322 $port = $this->Port;
1323 $tport = (integer)$hostinfo[4];
1324 if ($tport > 0 and $tport < 65536) {
1325 $port = $tport;
1326 }
1327 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1328 try {
1329 if ($this->Helo) {
1330 $hello = $this->Helo;
1331 } else {
1332 $hello = $this->serverHostname();
1333 }
1334 $this->smtp->hello($hello);
1335
1336 if ($tls) {
1337 if (!$this->smtp->startTLS()) {
1338 throw new phpmailerException($this->lang('connect_host'));
1339 }
1340 // We must resend HELO after tls negotiation
1341 $this->smtp->hello($hello);
1342 }
1343 if ($this->SMTPAuth) {
1344 if (!$this->smtp->authenticate(
1345 $this->Username,
1346 $this->Password,
1347 $this->AuthType,
1348 $this->Realm,
1349 $this->Workstation
1350 )
1351 ) {
1352 throw new phpmailerException($this->lang('authenticate'));
1353 }
1354 }
1355 return true;
1356 } catch (phpmailerException $exc) {
1357 $lastexception = $exc;
1358 // We must have connected, but then failed TLS or Auth, so close connection nicely
1359 $this->smtp->quit();
1360 }
1361 }
1362 }
1363 // If we get here, all connection attempts have failed, so close connection hard
1364 $this->smtp->close();
1365 // As we've caught all exceptions, just report whatever the last one was
1366 if ($this->exceptions and !is_null($lastexception)) {
1367 throw $lastexception;
1368 }
1369 return false;
1370 }
1371
1372 /**
1373 * Close the active SMTP session if one exists.
1374 * @return void
1375 */
1376 public function smtpClose()
1377 {
1378 if ($this->smtp !== null) {
1379 if ($this->smtp->connected()) {
1380 $this->smtp->quit();
1381 $this->smtp->close();
1382 }
1383 }
1384 }
1385
1386 /**
1387 * Set the language for error messages.
1388 * Returns false if it cannot load the language file.
1389 * The default language is English.
1390 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1391 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1392 * @return boolean
1393 * @access public
1394 */
1395 public function setLanguage($langcode = 'en', $lang_path = '')
1396 {
1397 // Define full set of translatable strings in English
1398 $PHPMAILER_LANG = array(
1399 'authenticate' => 'SMTP Error: Could not authenticate.',
1400 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1401 'data_not_accepted' => 'SMTP Error: data not accepted.',
1402 'empty_message' => 'Message body empty',
1403 'encoding' => 'Unknown encoding: ',
1404 'execute' => 'Could not execute: ',
1405 'file_access' => 'Could not access file: ',
1406 'file_open' => 'File Error: Could not open file: ',
1407 'from_failed' => 'The following From address failed: ',
1408 'instantiate' => 'Could not instantiate mail function.',
1409 'invalid_address' => 'Invalid address',
1410 'mailer_not_supported' => ' mailer is not supported.',
1411 'provide_address' => 'You must provide at least one recipient email address.',
1412 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1413 'signing' => 'Signing Error: ',
1414 'smtp_connect_failed' => 'SMTP connect() failed.',
1415 'smtp_error' => 'SMTP server error: ',
1416 'variable_set' => 'Cannot set or reset variable: '
1417 );
1418 if (empty($lang_path)) {
1419 // Calculate an absolute path so it can work if CWD is not here
1420 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1421 }
1422 $foundlang = true;
1423 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1424 if ($langcode != 'en') { // There is no English translation file
1425 // Make sure language file path is readable
1426 if (!is_readable($lang_file)) {
1427 $foundlang = false;
1428 } else {
1429 // Overwrite language-specific strings.
1430 // This way we'll never have missing translations.
1431 $foundlang = include $lang_file;
1432 }
1433 }
1434 $this->language = $PHPMAILER_LANG;
1435 return (boolean)$foundlang; // Returns false if language not found
1436 }
1437
1438 /**
1439 * Get the array of strings for the current language.
1440 * @return array
1441 */
1442 public function getTranslations()
1443 {
1444 return $this->language;
1445 }
1446
1447 /**
1448 * Create recipient headers.
1449 * @access public
1450 * @param string $type
1451 * @param array $addr An array of recipient,
1452 * where each recipient is a 2-element indexed array with element 0 containing an address
1453 * and element 1 containing a name, like:
1454 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1455 * @return string
1456 */
1457 public function addrAppend($type, $addr)
1458 {
1459 $addresses = array();
1460 foreach ($addr as $address) {
1461 $addresses[] = $this->addrFormat($address);
1462 }
1463 return $type . ': ' . implode(', ', $addresses) . $this->LE;
1464 }
1465
1466 /**
1467 * Format an address for use in a message header.
1468 * @access public
1469 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1470 * like array('joe@example.com', 'Joe User')
1471 * @return string
1472 */
1473 public function addrFormat($addr)
1474 {
1475 if (empty($addr[1])) { // No name provided
1476 return $this->secureHeader($addr[0]);
1477 } else {
1478 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1479 $addr[0]
1480 ) . '>';
1481 }
1482 }
1483
1484 /**
1485 * Word-wrap message.
1486 * For use with mailers that do not automatically perform wrapping
1487 * and for quoted-printable encoded messages.
1488 * Original written by philippe.
1489 * @param string $message The message to wrap
1490 * @param integer $length The line length to wrap to
1491 * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1492 * @access public
1493 * @return string
1494 */
1495 public function wrapText($message, $length, $qp_mode = false)
1496 {
1497 $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
1498 // If utf-8 encoding is used, we will need to make sure we don't
1499 // split multibyte characters when we wrap
1500 $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1501 $lelen = strlen($this->LE);
1502 $crlflen = strlen(self::CRLF);
1503
1504 $message = $this->fixEOL($message);
1505 if (substr($message, -$lelen) == $this->LE) {
1506 $message = substr($message, 0, -$lelen);
1507 }
1508
1509 $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
1510 $message = '';
1511 for ($i = 0; $i < count($line); $i++) {
1512 $line_part = explode(' ', $line[$i]);
1513 $buf = '';
1514 for ($e = 0; $e < count($line_part); $e++) {
1515 $word = $line_part[$e];
1516 if ($qp_mode and (strlen($word) > $length)) {
1517 $space_left = $length - strlen($buf) - $crlflen;
1518 if ($e != 0) {
1519 if ($space_left > 20) {
1520 $len = $space_left;
1521 if ($is_utf8) {
1522 $len = $this->utf8CharBoundary($word, $len);
1523 } elseif (substr($word, $len - 1, 1) == '=') {
1524 $len--;
1525 } elseif (substr($word, $len - 2, 1) == '=') {
1526 $len -= 2;
1527 }
1528 $part = substr($word, 0, $len);
1529 $word = substr($word, $len);
1530 $buf .= ' ' . $part;
1531 $message .= $buf . sprintf('=%s', self::CRLF);
1532 } else {
1533 $message .= $buf . $soft_break;
1534 }
1535 $buf = '';
1536 }
1537 while (strlen($word) > 0) {
1538 if ($length <= 0) {
1539 break;
1540 }
1541 $len = $length;
1542 if ($is_utf8) {
1543 $len = $this->utf8CharBoundary($word, $len);
1544 } elseif (substr($word, $len - 1, 1) == '=') {
1545 $len--;
1546 } elseif (substr($word, $len - 2, 1) == '=') {
1547 $len -= 2;
1548 }
1549 $part = substr($word, 0, $len);
1550 $word = substr($word, $len);
1551
1552 if (strlen($word) > 0) {
1553 $message .= $part . sprintf('=%s', self::CRLF);
1554 } else {
1555 $buf = $part;
1556 }
1557 }
1558 } else {
1559 $buf_o = $buf;
1560 $buf .= ($e == 0) ? $word : (' ' . $word);
1561
1562 if (strlen($buf) > $length and $buf_o != '') {
1563 $message .= $buf_o . $soft_break;
1564 $buf = $word;
1565 }
1566 }
1567 }
1568 $message .= $buf . self::CRLF;
1569 }
1570
1571 return $message;
1572 }
1573
1574 /**
1575 * Find the last character boundary prior to $maxLength in a utf-8
1576 * quoted (printable) encoded string.
1577 * Original written by Colin Brown.
1578 * @access public
1579 * @param string $encodedText utf-8 QP text
1580 * @param integer $maxLength find last character boundary prior to this length
1581 * @return integer
1582 */
1583 public function utf8CharBoundary($encodedText, $maxLength)
1584 {
1585 $foundSplitPos = false;
1586 $lookBack = 3;
1587 while (!$foundSplitPos) {
1588 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1589 $encodedCharPos = strpos($lastChunk, '=');
1590 if (false !== $encodedCharPos) {
1591 // Found start of encoded character byte within $lookBack block.
1592 // Check the encoded byte value (the 2 chars after the '=')
1593 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1594 $dec = hexdec($hex);
1595 if ($dec < 128) { // Single byte character.
1596 // If the encoded char was found at pos 0, it will fit
1597 // otherwise reduce maxLength to start of the encoded char
1598 $maxLength = ($encodedCharPos == 0) ? $maxLength :
1599 $maxLength - ($lookBack - $encodedCharPos);
1600 $foundSplitPos = true;
1601 } elseif ($dec >= 192) { // First byte of a multi byte character
1602 // Reduce maxLength to split at start of character
1603 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1604 $foundSplitPos = true;
1605 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1606 $lookBack += 3;
1607 }
1608 } else {
1609 // No encoded character found
1610 $foundSplitPos = true;
1611 }
1612 }
1613 return $maxLength;
1614 }
1615
1616 /**
1617 * Set the body wrapping.
1618 * @access public
1619 * @return void
1620 */
1621 public function setWordWrap()
1622 {
1623 if ($this->WordWrap < 1) {
1624 return;
1625 }
1626
1627 switch ($this->message_type) {
1628 case 'alt':
1629 case 'alt_inline':
1630 case 'alt_attach':
1631 case 'alt_inline_attach':
1632 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1633 break;
1634 default:
1635 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1636 break;
1637 }
1638 }
1639
1640 /**
1641 * Assemble message headers.
1642 * @access public
1643 * @return string The assembled headers
1644 */
1645 public function createHeader()
1646 {
1647 $result = '';
1648
1649 // Set the boundaries
1650 $uniq_id = md5(uniqid(time()));
1651 $this->boundary[1] = 'b1_' . $uniq_id;
1652 $this->boundary[2] = 'b2_' . $uniq_id;
1653 $this->boundary[3] = 'b3_' . $uniq_id;
1654
1655 if ($this->MessageDate == '') {
1656 $this->MessageDate = self::rfcDate();
1657 }
1658 $result .= $this->headerLine('Date', $this->MessageDate);
1659
1660
1661 // To be created automatically by mail()
1662 if ($this->SingleTo) {
1663 if ($this->Mailer != 'mail') {
1664 foreach ($this->to as $toaddr) {
1665 $this->SingleToArray[] = $this->addrFormat($toaddr);
1666 }
1667 }
1668 } else {
1669 if (count($this->to) > 0) {
1670 if ($this->Mailer != 'mail') {
1671 $result .= $this->addrAppend('To', $this->to);
1672 }
1673 } elseif (count($this->cc) == 0) {
1674 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1675 }
1676 }
1677
1678 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1679
1680 // sendmail and mail() extract Cc from the header before sending
1681 if (count($this->cc) > 0) {
1682 $result .= $this->addrAppend('Cc', $this->cc);
1683 }
1684
1685 // sendmail and mail() extract Bcc from the header before sending
1686 if ((
1687 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1688 )
1689 and count($this->bcc) > 0
1690 ) {
1691 $result .= $this->addrAppend('Bcc', $this->bcc);
1692 }
1693
1694 if (count($this->ReplyTo) > 0) {
1695 $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1696 }
1697
1698 // mail() sets the subject itself
1699 if ($this->Mailer != 'mail') {
1700 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1701 }
1702
1703 if ($this->MessageID != '') {
1704 $this->lastMessageID = $this->MessageID;
1705 } else {
1706 $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
1707 }
1708 $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
1709 $result .= $this->headerLine('X-Priority', $this->Priority);
1710 if ($this->XMailer == '') {
1711 $result .= $this->headerLine(
1712 'X-Mailer',
1713 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
1714 );
1715 } else {
1716 $myXmailer = trim($this->XMailer);
1717 if ($myXmailer) {
1718 $result .= $this->headerLine('X-Mailer', $myXmailer);
1719 }
1720 }
1721
1722 if ($this->ConfirmReadingTo != '') {
1723 $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1724 }
1725
1726 // Add custom headers
1727 for ($index = 0; $index < count($this->CustomHeader); $index++) {
1728 $result .= $this->headerLine(
1729 trim($this->CustomHeader[$index][0]),
1730 $this->encodeHeader(trim($this->CustomHeader[$index][1]))
1731 );
1732 }
1733 if (!$this->sign_key_file) {
1734 $result .= $this->headerLine('MIME-Version', '1.0');
1735 $result .= $this->getMailMIME();
1736 }
1737
1738 return $result;
1739 }
1740
1741 /**
1742 * Get the message MIME type headers.
1743 * @access public
1744 * @return string
1745 */
1746 public function getMailMIME()
1747 {
1748 $result = '';
1749 $ismultipart = true;
1750 switch ($this->message_type) {
1751 case 'inline':
1752 $result .= $this->headerLine('Content-Type', 'multipart/related;');
1753 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1754 break;
1755 case 'attach':
1756 case 'inline_attach':
1757 case 'alt_attach':
1758 case 'alt_inline_attach':
1759 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1760 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1761 break;
1762 case 'alt':
1763 case 'alt_inline':
1764 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1765 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1766 break;
1767 default:
1768 // Catches case 'plain': and case '':
1769 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
1770 $ismultipart = false;
1771 break;
1772 }
1773 // RFC1341 part 5 says 7bit is assumed if not specified
1774 if ($this->Encoding != '7bit') {
1775 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
1776 if ($ismultipart) {
1777 if ($this->Encoding == '8bit') {
1778 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
1779 }
1780 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
1781 } else {
1782 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1783 }
1784 }
1785
1786 if ($this->Mailer != 'mail') {
1787 $result .= $this->LE;
1788 }
1789
1790 return $result;
1791 }
1792
1793 /**
1794 * Returns the whole MIME message.
1795 * Includes complete headers and body.
1796 * Only valid post preSend().
1797 * @see PHPMailer::preSend()
1798 * @access public
1799 * @return string
1800 */
1801 public function getSentMIMEMessage()
1802 {
1803 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1804 }
1805
1806
1807 /**
1808 * Assemble the message body.
1809 * Returns an empty string on failure.
1810 * @access public
1811 * @throws phpmailerException
1812 * @return string The assembled message body
1813 */
1814 public function createBody()
1815 {
1816 $body = '';
1817
1818 if ($this->sign_key_file) {
1819 $body .= $this->getMailMIME() . $this->LE;
1820 }
1821
1822 $this->setWordWrap();
1823
1824 $bodyEncoding = $this->Encoding;
1825 $bodyCharSet = $this->CharSet;
1826 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
1827 $bodyEncoding = '7bit';
1828 $bodyCharSet = 'us-ascii';
1829 }
1830 $altBodyEncoding = $this->Encoding;
1831 $altBodyCharSet = $this->CharSet;
1832 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
1833 $altBodyEncoding = '7bit';
1834 $altBodyCharSet = 'us-ascii';
1835 }
1836 switch ($this->message_type) {
1837 case 'inline':
1838 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1839 $body .= $this->encodeString($this->Body, $bodyEncoding);
1840 $body .= $this->LE . $this->LE;
1841 $body .= $this->attachAll('inline', $this->boundary[1]);
1842 break;
1843 case 'attach':
1844 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1845 $body .= $this->encodeString($this->Body, $bodyEncoding);
1846 $body .= $this->LE . $this->LE;
1847 $body .= $this->attachAll('attachment', $this->boundary[1]);
1848 break;
1849 case 'inline_attach':
1850 $body .= $this->textLine('--' . $this->boundary[1]);
1851 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1852 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1853 $body .= $this->LE;
1854 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
1855 $body .= $this->encodeString($this->Body, $bodyEncoding);
1856 $body .= $this->LE . $this->LE;
1857 $body .= $this->attachAll('inline', $this->boundary[2]);
1858 $body .= $this->LE;
1859 $body .= $this->attachAll('attachment', $this->boundary[1]);
1860 break;
1861 case 'alt':
1862 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1863 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1864 $body .= $this->LE . $this->LE;
1865 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
1866 $body .= $this->encodeString($this->Body, $bodyEncoding);
1867 $body .= $this->LE . $this->LE;
1868 if (!empty($this->Ical)) {
1869 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1870 $body .= $this->encodeString($this->Ical, $this->Encoding);
1871 $body .= $this->LE . $this->LE;
1872 }
1873 $body .= $this->endBoundary($this->boundary[1]);
1874 break;
1875 case 'alt_inline':
1876 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1877 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1878 $body .= $this->LE . $this->LE;
1879 $body .= $this->textLine('--' . $this->boundary[1]);
1880 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1881 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1882 $body .= $this->LE;
1883 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1884 $body .= $this->encodeString($this->Body, $bodyEncoding);
1885 $body .= $this->LE . $this->LE;
1886 $body .= $this->attachAll('inline', $this->boundary[2]);
1887 $body .= $this->LE;
1888 $body .= $this->endBoundary($this->boundary[1]);
1889 break;
1890 case 'alt_attach':
1891 $body .= $this->textLine('--' . $this->boundary[1]);
1892 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1893 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1894 $body .= $this->LE;
1895 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1896 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1897 $body .= $this->LE . $this->LE;
1898 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1899 $body .= $this->encodeString($this->Body, $bodyEncoding);
1900 $body .= $this->LE . $this->LE;
1901 $body .= $this->endBoundary($this->boundary[2]);
1902 $body .= $this->LE;
1903 $body .= $this->attachAll('attachment', $this->boundary[1]);
1904 break;
1905 case 'alt_inline_attach':
1906 $body .= $this->textLine('--' . $this->boundary[1]);
1907 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1908 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1909 $body .= $this->LE;
1910 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1911 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1912 $body .= $this->LE . $this->LE;
1913 $body .= $this->textLine('--' . $this->boundary[2]);
1914 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1915 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
1916 $body .= $this->LE;
1917 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
1918 $body .= $this->encodeString($this->Body, $bodyEncoding);
1919 $body .= $this->LE . $this->LE;
1920 $body .= $this->attachAll('inline', $this->boundary[3]);
1921 $body .= $this->LE;
1922 $body .= $this->endBoundary($this->boundary[2]);
1923 $body .= $this->LE;
1924 $body .= $this->attachAll('attachment', $this->boundary[1]);
1925 break;
1926 default:
1927 // catch case 'plain' and case ''
1928 $body .= $this->encodeString($this->Body, $bodyEncoding);
1929 break;
1930 }
1931
1932 if ($this->isError()) {
1933 $body = '';
1934 } elseif ($this->sign_key_file) {
1935 try {
1936 if (!defined('PKCS7_TEXT')) {
1937 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
1938 }
1939 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
1940 $file = tempnam(sys_get_temp_dir(), 'mail');
1941 if (false === file_put_contents($file, $body)) {
1942 throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
1943 }
1944 $signed = tempnam(sys_get_temp_dir(), 'signed');
1945 if (@openssl_pkcs7_sign(
1946 $file,
1947 $signed,
1948 'file://' . realpath($this->sign_cert_file),
1949 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
1950 null
1951 )
1952 ) {
1953 @unlink($file);
1954 $body = file_get_contents($signed);
1955 @unlink($signed);
1956 } else {
1957 @unlink($file);
1958 @unlink($signed);
1959 throw new phpmailerException($this->lang('signing') . openssl_error_string());
1960 }
1961 } catch (phpmailerException $exc) {
1962 $body = '';
1963 if ($this->exceptions) {
1964 throw $exc;
1965 }
1966 }
1967 }
1968 return $body;
1969 }
1970
1971 /**
1972 * Return the start of a message boundary.
1973 * @access protected
1974 * @param string $boundary
1975 * @param string $charSet
1976 * @param string $contentType
1977 * @param string $encoding
1978 * @return string
1979 */
1980 protected function getBoundary($boundary, $charSet, $contentType, $encoding)
1981 {
1982 $result = '';
1983 if ($charSet == '') {
1984 $charSet = $this->CharSet;
1985 }
1986 if ($contentType == '') {
1987 $contentType = $this->ContentType;
1988 }
1989 if ($encoding == '') {
1990 $encoding = $this->Encoding;
1991 }
1992 $result .= $this->textLine('--' . $boundary);
1993 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
1994 $result .= $this->LE;
1995 // RFC1341 part 5 says 7bit is assumed if not specified
1996 if ($encoding != '7bit') {
1997 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
1998 }
1999 $result .= $this->LE;
2000
2001 return $result;
2002 }
2003
2004 /**
2005 * Return the end of a message boundary.
2006 * @access protected
2007 * @param string $boundary
2008 * @return string
2009 */
2010 protected function endBoundary($boundary)
2011 {
2012 return $this->LE . '--' . $boundary . '--' . $this->LE;
2013 }
2014
2015 /**
2016 * Set the message type.
2017 * PHPMailer only supports some preset message types,
2018 * not arbitrary MIME structures.
2019 * @access protected
2020 * @return void
2021 */
2022 protected function setMessageType()
2023 {
2024 $type = array();
2025 if ($this->alternativeExists()) {
2026 $type[] = 'alt';
2027 }
2028 if ($this->inlineImageExists()) {
2029 $type[] = 'inline';
2030 }
2031 if ($this->attachmentExists()) {
2032 $type[] = 'attach';
2033 }
2034 $this->message_type = implode('_', $type);
2035 if ($this->message_type == '') {
2036 $this->message_type = 'plain';
2037 }
2038 }
2039
2040 /**
2041 * Format a header line.
2042 * @access public
2043 * @param string $name
2044 * @param string $value
2045 * @return string
2046 */
2047 public function headerLine($name, $value)
2048 {
2049 return $name . ': ' . $value . $this->LE;
2050 }
2051
2052 /**
2053 * Return a formatted mail line.
2054 * @access public
2055 * @param string $value
2056 * @return string
2057 */
2058 public function textLine($value)
2059 {
2060 return $value . $this->LE;
2061 }
2062
2063 /**
2064 * Add an attachment from a path on the filesystem.
2065 * Returns false if the file could not be found or read.
2066 * @param string $path Path to the attachment.
2067 * @param string $name Overrides the attachment name.
2068 * @param string $encoding File encoding (see $Encoding).
2069 * @param string $type File extension (MIME) type.
2070 * @param string $disposition Disposition to use
2071 * @throws phpmailerException
2072 * @return boolean
2073 */
2074 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2075 {
2076 try {
2077 if (!@is_file($path)) {
2078 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2079 }
2080
2081 // If a MIME type is not specified, try to work it out from the file name
2082 if ($type == '') {
2083 $type = self::filenameToType($path);
2084 }
2085
2086 $filename = basename($path);
2087 if ($name == '') {
2088 $name = $filename;
2089 }
2090
2091 $this->attachment[] = array(
2092 0 => $path,
2093 1 => $filename,
2094 2 => $name,
2095 3 => $encoding,
2096 4 => $type,
2097 5 => false, // isStringAttachment
2098 6 => $disposition,
2099 7 => 0
2100 );
2101
2102 } catch (phpmailerException $exc) {
2103 $this->setError($exc->getMessage());
2104 $this->edebug($exc->getMessage());
2105 if ($this->exceptions) {
2106 throw $exc;
2107 }
2108 return false;
2109 }
2110 return true;
2111 }
2112
2113 /**
2114 * Return the array of attachments.
2115 * @return array
2116 */
2117 public function getAttachments()
2118 {
2119 return $this->attachment;
2120 }
2121
2122 /**
2123 * Attach all file, string, and binary attachments to the message.
2124 * Returns an empty string on failure.
2125 * @access protected
2126 * @param string $disposition_type
2127 * @param string $boundary
2128 * @return string
2129 */
2130 protected function attachAll($disposition_type, $boundary)
2131 {
2132 // Return text of body
2133 $mime = array();
2134 $cidUniq = array();
2135 $incl = array();
2136
2137 // Add all attachments
2138 foreach ($this->attachment as $attachment) {
2139 // Check if it is a valid disposition_filter
2140 if ($attachment[6] == $disposition_type) {
2141 // Check for string attachment
2142 $string = '';
2143 $path = '';
2144 $bString = $attachment[5];
2145 if ($bString) {
2146 $string = $attachment[0];
2147 } else {
2148 $path = $attachment[0];
2149 }
2150
2151 $inclhash = md5(serialize($attachment));
2152 if (in_array($inclhash, $incl)) {
2153 continue;
2154 }
2155 $incl[] = $inclhash;
2156 $name = $attachment[2];
2157 $encoding = $attachment[3];
2158 $type = $attachment[4];
2159 $disposition = $attachment[6];
2160 $cid = $attachment[7];
2161 if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2162 continue;
2163 }
2164 $cidUniq[$cid] = true;
2165
2166 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2167 $mime[] = sprintf(
2168 'Content-Type: %s; name="%s"%s',
2169 $type,
2170 $this->encodeHeader($this->secureHeader($name)),
2171 $this->LE
2172 );
2173 // RFC1341 part 5 says 7bit is assumed if not specified
2174 if ($encoding != '7bit') {
2175 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2176 }
2177
2178 if ($disposition == 'inline') {
2179 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2180 }
2181
2182 // If a filename contains any of these chars, it should be quoted,
2183 // but not otherwise: RFC2183 & RFC2045 5.1
2184 // Fixes a warning in IETF's msglint MIME checker
2185 // Allow for bypassing the Content-Disposition header totally
2186 if (!(empty($disposition))) {
2187 $encoded_name = $this->encodeHeader($this->secureHeader($name));
2188 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2189 $mime[] = sprintf(
2190 'Content-Disposition: %s; filename="%s"%s',
2191 $disposition,
2192 $encoded_name,
2193 $this->LE . $this->LE
2194 );
2195 } else {
2196 $mime[] = sprintf(
2197 'Content-Disposition: %s; filename=%s%s',
2198 $disposition,
2199 $encoded_name,
2200 $this->LE . $this->LE
2201 );
2202 }
2203 } else {
2204 $mime[] = $this->LE;
2205 }
2206
2207 // Encode as string attachment
2208 if ($bString) {
2209 $mime[] = $this->encodeString($string, $encoding);
2210 if ($this->isError()) {
2211 return '';
2212 }
2213 $mime[] = $this->LE . $this->LE;
2214 } else {
2215 $mime[] = $this->encodeFile($path, $encoding);
2216 if ($this->isError()) {
2217 return '';
2218 }
2219 $mime[] = $this->LE . $this->LE;
2220 }
2221 }
2222 }
2223
2224 $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2225
2226 return implode('', $mime);
2227 }
2228
2229 /**
2230 * Encode a file attachment in requested format.
2231 * Returns an empty string on failure.
2232 * @param string $path The full path to the file
2233 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2234 * @throws phpmailerException
2235 * @see EncodeFile(encodeFile
2236 * @access protected
2237 * @return string
2238 */
2239 protected function encodeFile($path, $encoding = 'base64')
2240 {
2241 try {
2242 if (!is_readable($path)) {
2243 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2244 }
2245 $magic_quotes = get_magic_quotes_runtime();
2246 if ($magic_quotes) {
2247 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2248 set_magic_quotes_runtime(false);
2249 } else {
2250 //Doesn't exist in PHP 5.4, but we don't need to check because
2251 //get_magic_quotes_runtime always returns false in 5.4+
2252 //so it will never get here
2253 ini_set('magic_quotes_runtime', 0);
2254 }
2255 }
2256 $file_buffer = file_get_contents($path);
2257 $file_buffer = $this->encodeString($file_buffer, $encoding);
2258 if ($magic_quotes) {
2259 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2260 set_magic_quotes_runtime($magic_quotes);
2261 } else {
2262 ini_set('magic_quotes_runtime', ($magic_quotes?'1':'0'));
2263 }
2264 }
2265 return $file_buffer;
2266 } catch (Exception $exc) {
2267 $this->setError($exc->getMessage());
2268 return '';
2269 }
2270 }
2271
2272 /**
2273 * Encode a string in requested format.
2274 * Returns an empty string on failure.
2275 * @param string $str The text to encode
2276 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2277 * @access public
2278 * @return string
2279 */
2280 public function encodeString($str, $encoding = 'base64')
2281 {
2282 $encoded = '';
2283 switch (strtolower($encoding)) {
2284 case 'base64':
2285 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2286 break;
2287 case '7bit':
2288 case '8bit':
2289 $encoded = $this->fixEOL($str);
2290 // Make sure it ends with a line break
2291 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2292 $encoded .= $this->LE;
2293 }
2294 break;
2295 case 'binary':
2296 $encoded = $str;
2297 break;
2298 case 'quoted-printable':
2299 $encoded = $this->encodeQP($str);
2300 break;
2301 default:
2302 $this->setError($this->lang('encoding') . $encoding);
2303 break;
2304 }
2305 return $encoded;
2306 }
2307
2308 /**
2309 * Encode a header string optimally.
2310 * Picks shortest of Q, B, quoted-printable or none.
2311 * @access public
2312 * @param string $str
2313 * @param string $position
2314 * @return string
2315 */
2316 public function encodeHeader($str, $position = 'text')
2317 {
2318 $matchcount = 0;
2319 switch (strtolower($position)) {
2320 case 'phrase':
2321 if (!preg_match('/[\200-\377]/', $str)) {
2322 // Can't use addslashes as we don't know the value of magic_quotes_sybase
2323 $encoded = addcslashes($str, "\0..\37\177\\\"");
2324 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2325 return ($encoded);
2326 } else {
2327 return ("\"$encoded\"");
2328 }
2329 }
2330 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2331 break;
2332 /** @noinspection PhpMissingBreakStatementInspection */
2333 case 'comment':
2334 $matchcount = preg_match_all('/[()"]/', $str, $matches);
2335 // Intentional fall-through
2336 case 'text':
2337 default:
2338 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2339 break;
2340 }
2341
2342 if ($matchcount == 0) { // There are no chars that need encoding
2343 return ($str);
2344 }
2345
2346 $maxlen = 75 - 7 - strlen($this->CharSet);
2347 // Try to select the encoding which should produce the shortest output
2348 if ($matchcount > strlen($str) / 3) {
2349 // More than a third of the content will need encoding, so B encoding will be most efficient
2350 $encoding = 'B';
2351 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2352 // Use a custom function which correctly encodes and wraps long
2353 // multibyte strings without breaking lines within a character
2354 $encoded = $this->base64EncodeWrapMB($str, "\n");
2355 } else {
2356 $encoded = base64_encode($str);
2357 $maxlen -= $maxlen % 4;
2358 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2359 }
2360 } else {
2361 $encoding = 'Q';
2362 $encoded = $this->encodeQ($str, $position);
2363 $encoded = $this->wrapText($encoded, $maxlen, true);
2364 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2365 }
2366
2367 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2368 $encoded = trim(str_replace("\n", $this->LE, $encoded));
2369
2370 return $encoded;
2371 }
2372
2373 /**
2374 * Check if a string contains multi-byte characters.
2375 * @access public
2376 * @param string $str multi-byte text to wrap encode
2377 * @return boolean
2378 */
2379 public function hasMultiBytes($str)
2380 {
2381 if (function_exists('mb_strlen')) {
2382 return (strlen($str) > mb_strlen($str, $this->CharSet));
2383 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2384 return false;
2385 }
2386 }
2387
2388 /**
2389 * Does a string contain any 8-bit chars (in any charset)?
2390 * @param string $text
2391 * @return boolean
2392 */
2393 public function has8bitChars($text)
2394 {
2395 return (boolean)preg_match('/[\x80-\xFF]/', $text);
2396 }
2397
2398 /**
2399 * Encode and wrap long multibyte strings for mail headers
2400 * without breaking lines within a character.
2401 * Adapted from a function by paravoid
2402 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2403 * @access public
2404 * @param string $str multi-byte text to wrap encode
2405 * @param string $linebreak string to use as linefeed/end-of-line
2406 * @return string
2407 */
2408 public function base64EncodeWrapMB($str, $linebreak = null)
2409 {
2410 $start = '=?' . $this->CharSet . '?B?';
2411 $end = '?=';
2412 $encoded = '';
2413 if ($linebreak === null) {
2414 $linebreak = $this->LE;
2415 }
2416
2417 $mb_length = mb_strlen($str, $this->CharSet);
2418 // Each line must have length <= 75, including $start and $end
2419 $length = 75 - strlen($start) - strlen($end);
2420 // Average multi-byte ratio
2421 $ratio = $mb_length / strlen($str);
2422 // Base64 has a 4:3 ratio
2423 $avgLength = floor($length * $ratio * .75);
2424
2425 for ($i = 0; $i < $mb_length; $i += $offset) {
2426 $lookBack = 0;
2427 do {
2428 $offset = $avgLength - $lookBack;
2429 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2430 $chunk = base64_encode($chunk);
2431 $lookBack++;
2432 } while (strlen($chunk) > $length);
2433 $encoded .= $chunk . $linebreak;
2434 }
2435
2436 // Chomp the last linefeed
2437 $encoded = substr($encoded, 0, -strlen($linebreak));
2438 return $encoded;
2439 }
2440
2441 /**
2442 * Encode a string in quoted-printable format.
2443 * According to RFC2045 section 6.7.
2444 * @access public
2445 * @param string $string The text to encode
2446 * @param integer $line_max Number of chars allowed on a line before wrapping
2447 * @return string
2448 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2449 */
2450 public function encodeQP($string, $line_max = 76)
2451 {
2452 if (function_exists('quoted_printable_encode')) { // Use native function if it's available (>= PHP5.3)
2453 return $this->fixEOL(quoted_printable_encode($string));
2454 }
2455 // Fall back to a pure PHP implementation
2456 $string = str_replace(
2457 array('%20', '%0D%0A.', '%0D%0A', '%'),
2458 array(' ', "\r\n=2E", "\r\n", '='),
2459 rawurlencode($string)
2460 );
2461 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2462 return $this->fixEOL($string);
2463 }
2464
2465 /**
2466 * Backward compatibility wrapper for an old QP encoding function that was removed.
2467 * @see PHPMailer::encodeQP()
2468 * @access public
2469 * @param string $string
2470 * @param integer $line_max
2471 * @param boolean $space_conv
2472 * @return string
2473 * @deprecated Use encodeQP instead.
2474 */
2475 public function encodeQPphp(
2476 $string,
2477 $line_max = 76,
2478 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2479 ) {
2480 return $this->encodeQP($string, $line_max);
2481 }
2482
2483 /**
2484 * Encode a string using Q encoding.
2485 * @link http://tools.ietf.org/html/rfc2047
2486 * @param string $str the text to encode
2487 * @param string $position Where the text is going to be used, see the RFC for what that means
2488 * @access public
2489 * @return string
2490 */
2491 public function encodeQ($str, $position = 'text')
2492 {
2493 // There should not be any EOL in the string
2494 $pattern = '';
2495 $encoded = str_replace(array("\r", "\n"), '', $str);
2496 switch (strtolower($position)) {
2497 case 'phrase':
2498 // RFC 2047 section 5.3
2499 $pattern = '^A-Za-z0-9!*+\/ -';
2500 break;
2501 /** @noinspection PhpMissingBreakStatementInspection */
2502 case 'comment':
2503 // RFC 2047 section 5.2
2504 $pattern = '\(\)"';
2505 // intentional fall-through
2506 // for this reason we build the $pattern without including delimiters and []
2507 case 'text':
2508 default:
2509 // RFC 2047 section 5.1
2510 // Replace every high ascii, control, =, ? and _ characters
2511 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2512 break;
2513 }
2514 $matches = array();
2515 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2516 // If the string contains an '=', make sure it's the first thing we replace
2517 // so as to avoid double-encoding
2518 $eqkey = array_search('=', $matches[0]);
2519 if (false !== $eqkey) {
2520 unset($matches[0][$eqkey]);
2521 array_unshift($matches[0], '=');
2522 }
2523 foreach (array_unique($matches[0]) as $char) {
2524 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2525 }
2526 }
2527 // Replace every spaces to _ (more readable than =20)
2528 return str_replace(' ', '_', $encoded);
2529 }
2530
2531
2532 /**
2533 * Add a string or binary attachment (non-filesystem).
2534 * This method can be used to attach ascii or binary data,
2535 * such as a BLOB record from a database.
2536 * @param string $string String attachment data.
2537 * @param string $filename Name of the attachment.
2538 * @param string $encoding File encoding (see $Encoding).
2539 * @param string $type File extension (MIME) type.
2540 * @param string $disposition Disposition to use
2541 * @return void
2542 */
2543 public function addStringAttachment(
2544 $string,
2545 $filename,
2546 $encoding = 'base64',
2547 $type = '',
2548 $disposition = 'attachment'
2549 ) {
2550 // If a MIME type is not specified, try to work it out from the file name
2551 if ($type == '') {
2552 $type = self::filenameToType($filename);
2553 }
2554 // Append to $attachment array
2555 $this->attachment[] = array(
2556 0 => $string,
2557 1 => $filename,
2558 2 => basename($filename),
2559 3 => $encoding,
2560 4 => $type,
2561 5 => true, // isStringAttachment
2562 6 => $disposition,
2563 7 => 0
2564 );
2565 }
2566
2567 /**
2568 * Add an embedded (inline) attachment from a file.
2569 * This can include images, sounds, and just about any other document type.
2570 * These differ from 'regular' attachments in that they are intended to be
2571 * displayed inline with the message, not just attached for download.
2572 * This is used in HTML messages that embed the images
2573 * the HTML refers to using the $cid value.
2574 * @param string $path Path to the attachment.
2575 * @param string $cid Content ID of the attachment; Use this to reference
2576 * the content when using an embedded image in HTML.
2577 * @param string $name Overrides the attachment name.
2578 * @param string $encoding File encoding (see $Encoding).
2579 * @param string $type File MIME type.
2580 * @param string $disposition Disposition to use
2581 * @return boolean True on successfully adding an attachment
2582 */
2583 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2584 {
2585 if (!@is_file($path)) {
2586 $this->setError($this->lang('file_access') . $path);
2587 return false;
2588 }
2589
2590 // If a MIME type is not specified, try to work it out from the file name
2591 if ($type == '') {
2592 $type = self::filenameToType($path);
2593 }
2594
2595 $filename = basename($path);
2596 if ($name == '') {
2597 $name = $filename;
2598 }
2599
2600 // Append to $attachment array
2601 $this->attachment[] = array(
2602 0 => $path,
2603 1 => $filename,
2604 2 => $name,
2605 3 => $encoding,
2606 4 => $type,
2607 5 => false, // isStringAttachment
2608 6 => $disposition,
2609 7 => $cid
2610 );
2611 return true;
2612 }
2613
2614 /**
2615 * Add an embedded stringified attachment.
2616 * This can include images, sounds, and just about any other document type.
2617 * Be sure to set the $type to an image type for images:
2618 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2619 * @param string $string The attachment binary data.
2620 * @param string $cid Content ID of the attachment; Use this to reference
2621 * the content when using an embedded image in HTML.
2622 * @param string $name
2623 * @param string $encoding File encoding (see $Encoding).
2624 * @param string $type MIME type.
2625 * @param string $disposition Disposition to use
2626 * @return boolean True on successfully adding an attachment
2627 */
2628 public function addStringEmbeddedImage(
2629 $string,
2630 $cid,
2631 $name = '',
2632 $encoding = 'base64',
2633 $type = '',
2634 $disposition = 'inline'
2635 ) {
2636 // If a MIME type is not specified, try to work it out from the name
2637 if ($type == '') {
2638 $type = self::filenameToType($name);
2639 }
2640
2641 // Append to $attachment array
2642 $this->attachment[] = array(
2643 0 => $string,
2644 1 => $name,
2645 2 => $name,
2646 3 => $encoding,
2647 4 => $type,
2648 5 => true, // isStringAttachment
2649 6 => $disposition,
2650 7 => $cid
2651 );
2652 return true;
2653 }
2654
2655 /**
2656 * Check if an inline attachment is present.
2657 * @access public
2658 * @return boolean
2659 */
2660 public function inlineImageExists()
2661 {
2662 foreach ($this->attachment as $attachment) {
2663 if ($attachment[6] == 'inline') {
2664 return true;
2665 }
2666 }
2667 return false;
2668 }
2669
2670 /**
2671 * Check if an attachment (non-inline) is present.
2672 * @return boolean
2673 */
2674 public function attachmentExists()
2675 {
2676 foreach ($this->attachment as $attachment) {
2677 if ($attachment[6] == 'attachment') {
2678 return true;
2679 }
2680 }
2681 return false;
2682 }
2683
2684 /**
2685 * Check if this message has an alternative body set.
2686 * @return boolean
2687 */
2688 public function alternativeExists()
2689 {
2690 return !empty($this->AltBody);
2691 }
2692
2693 /**
2694 * Clear all To recipients.
2695 * @return void
2696 */
2697 public function clearAddresses()
2698 {
2699 foreach ($this->to as $to) {
2700 unset($this->all_recipients[strtolower($to[0])]);
2701 }
2702 $this->to = array();
2703 }
2704
2705 /**
2706 * Clear all CC recipients.
2707 * @return void
2708 */
2709 public function clearCCs()
2710 {
2711 foreach ($this->cc as $cc) {
2712 unset($this->all_recipients[strtolower($cc[0])]);
2713 }
2714 $this->cc = array();
2715 }
2716
2717 /**
2718 * Clear all BCC recipients.
2719 * @return void
2720 */
2721 public function clearBCCs()
2722 {
2723 foreach ($this->bcc as $bcc) {
2724 unset($this->all_recipients[strtolower($bcc[0])]);
2725 }
2726 $this->bcc = array();
2727 }
2728
2729 /**
2730 * Clear all ReplyTo recipients.
2731 * @return void
2732 */
2733 public function clearReplyTos()
2734 {
2735 $this->ReplyTo = array();
2736 }
2737
2738 /**
2739 * Clear all recipient types.
2740 * @return void
2741 */
2742 public function clearAllRecipients()
2743 {
2744 $this->to = array();
2745 $this->cc = array();
2746 $this->bcc = array();
2747 $this->all_recipients = array();
2748 }
2749
2750 /**
2751 * Clear all filesystem, string, and binary attachments.
2752 * @return void
2753 */
2754 public function clearAttachments()
2755 {
2756 $this->attachment = array();
2757 }
2758
2759 /**
2760 * Clear all custom headers.
2761 * @return void
2762 */
2763 public function clearCustomHeaders()
2764 {
2765 $this->CustomHeader = array();
2766 }
2767
2768 /**
2769 * Add an error message to the error container.
2770 * @access protected
2771 * @param string $msg
2772 * @return void
2773 */
2774 protected function setError($msg)
2775 {
2776 $this->error_count++;
2777 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2778 $lasterror = $this->smtp->getError();
2779 if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2780 $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
2781 }
2782 }
2783 $this->ErrorInfo = $msg;
2784 }
2785
2786 /**
2787 * Return an RFC 822 formatted date.
2788 * @access public
2789 * @return string
2790 * @static
2791 */
2792 public static function rfcDate()
2793 {
2794 // Set the time zone to whatever the default is to avoid 500 errors
2795 // Will default to UTC if it's not set properly in php.ini
2796 date_default_timezone_set(@date_default_timezone_get());
2797 return date('D, j M Y H:i:s O');
2798 }
2799
2800 /**
2801 * Get the server hostname.
2802 * Returns 'localhost.localdomain' if unknown.
2803 * @access protected
2804 * @return string
2805 */
2806 protected function serverHostname()
2807 {
2808 $result = 'localhost.localdomain';
2809 if (!empty($this->Hostname)) {
2810 $result = $this->Hostname;
2811 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
2812 $result = $_SERVER['SERVER_NAME'];
2813 } elseif (function_exists('gethostname') && gethostname() !== false) {
2814 $result = gethostname();
2815 } elseif (php_uname('n') !== false) {
2816 $result = php_uname('n');
2817 }
2818 return $result;
2819 }
2820
2821 /**
2822 * Get an error message in the current language.
2823 * @access protected
2824 * @param string $key
2825 * @return string
2826 */
2827 protected function lang($key)
2828 {
2829 if (count($this->language) < 1) {
2830 $this->setLanguage('en'); // set the default language
2831 }
2832
2833 if (isset($this->language[$key])) {
2834 return $this->language[$key];
2835 } else {
2836 return 'Language string failed to load: ' . $key;
2837 }
2838 }
2839
2840 /**
2841 * Check if an error occurred.
2842 * @access public
2843 * @return boolean True if an error did occur.
2844 */
2845 public function isError()
2846 {
2847 return ($this->error_count > 0);
2848 }
2849
2850 /**
2851 * Ensure consistent line endings in a string.
2852 * Changes every end of line from CRLF, CR or LF to $this->LE.
2853 * @access public
2854 * @param string $str String to fixEOL
2855 * @return string
2856 */
2857 public function fixEOL($str)
2858 {
2859 // Normalise to \n
2860 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
2861 // Now convert LE as needed
2862 if ($this->LE !== "\n") {
2863 $nstr = str_replace("\n", $this->LE, $nstr);
2864 }
2865 return $nstr;
2866 }
2867
2868 /**
2869 * Add a custom header.
2870 * $name value can be overloaded to contain
2871 * both header name and value (name:value)
2872 * @access public
2873 * @param string $name Custom header name
2874 * @param string $value Header value
2875 * @return void
2876 */
2877 public function addCustomHeader($name, $value = null)
2878 {
2879 if ($value === null) {
2880 // Value passed in as name:value
2881 $this->CustomHeader[] = explode(':', $name, 2);
2882 } else {
2883 $this->CustomHeader[] = array($name, $value);
2884 }
2885 }
2886
2887 /**
2888 * Create a message from an HTML string.
2889 * Automatically makes modifications for inline images and backgrounds
2890 * and creates a plain-text version by converting the HTML.
2891 * Overwrites any existing values in $this->Body and $this->AltBody
2892 * @access public
2893 * @param string $message HTML message string
2894 * @param string $basedir baseline directory for path
2895 * @param boolean|callable $advanced Whether to use the internal HTML to text converter
2896 * or your own custom converter @see html2text()
2897 * @return string $message
2898 */
2899 public function msgHTML($message, $basedir = '', $advanced = false)
2900 {
2901 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
2902 if (isset($images[2])) {
2903 foreach ($images[2] as $imgindex => $url) {
2904 // Convert data URIs into embedded images
2905 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
2906 $data = substr($url, strpos($url, ','));
2907 if ($match[2]) {
2908 $data = base64_decode($data);
2909 } else {
2910 $data = rawurldecode($data);
2911 }
2912 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2913 if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) {
2914 $message = str_replace(
2915 $images[0][$imgindex],
2916 $images[1][$imgindex] . '="cid:' . $cid . '"',
2917 $message
2918 );
2919 }
2920 } elseif (!preg_match('#^[A-z]+://#', $url)) {
2921 // Do not change urls for absolute images (thanks to corvuscorax)
2922 $filename = basename($url);
2923 $directory = dirname($url);
2924 if ($directory == '.') {
2925 $directory = '';
2926 }
2927 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2928 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2929 $basedir .= '/';
2930 }
2931 if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2932 $directory .= '/';
2933 }
2934 if ($this->addEmbeddedImage(
2935 $basedir . $directory . $filename,
2936 $cid,
2937 $filename,
2938 'base64',
2939 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
2940 )
2941 ) {
2942 $message = preg_replace(
2943 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
2944 $images[1][$imgindex] . '="cid:' . $cid . '"',
2945 $message
2946 );
2947 }
2948 }
2949 }
2950 }
2951 $this->isHTML(true);
2952 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2953 $this->Body = $this->normalizeBreaks($message);
2954 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
2955 if (empty($this->AltBody)) {
2956 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
2957 self::CRLF . self::CRLF;
2958 }
2959 return $this->Body;
2960 }
2961
2962 /**
2963 * Convert an HTML string into plain text.
2964 * This is used by msgHTML().
2965 * Note - older versions of this function used a bundled advanced converter
2966 * which was been removed for license reasons in #232
2967 * Example usage:
2968 * <code>
2969 * // Use default conversion
2970 * $plain = $mail->html2text($html);
2971 * // Use your own custom converter
2972 * $plain = $mail->html2text($html, function($html) {
2973 * $converter = new MyHtml2text($html);
2974 * return $converter->get_text();
2975 * });
2976 * </code>
2977 * @param string $html The HTML text to convert
2978 * @param boolean|callable $advanced Any boolean value to use the internal converter,
2979 * or provide your own callable for custom conversion.
2980 * @return string
2981 */
2982 public function html2text($html, $advanced = false)
2983 {
2984 if (is_callable($advanced)) {
2985 return call_user_func($advanced, $html);
2986 }
2987 return html_entity_decode(
2988 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
2989 ENT_QUOTES,
2990 $this->CharSet
2991 );
2992 }
2993
2994 /**
2995 * Get the MIME type for a file extension.
2996 * @param string $ext File extension
2997 * @access public
2998 * @return string MIME type of file.
2999 * @static
3000 */
3001 public static function _mime_types($ext = '')
3002 {
3003 $mimes = array(
3004 'xl' => 'application/excel',
3005 'js' => 'application/javascript',
3006 'hqx' => 'application/mac-binhex40',
3007 'cpt' => 'application/mac-compactpro',
3008 'bin' => 'application/macbinary',
3009 'doc' => 'application/msword',
3010 'word' => 'application/msword',
3011 'class' => 'application/octet-stream',
3012 'dll' => 'application/octet-stream',
3013 'dms' => 'application/octet-stream',
3014 'exe' => 'application/octet-stream',
3015 'lha' => 'application/octet-stream',
3016 'lzh' => 'application/octet-stream',
3017 'psd' => 'application/octet-stream',
3018 'sea' => 'application/octet-stream',
3019 'so' => 'application/octet-stream',
3020 'oda' => 'application/oda',
3021 'pdf' => 'application/pdf',
3022 'ai' => 'application/postscript',
3023 'eps' => 'application/postscript',
3024 'ps' => 'application/postscript',
3025 'smi' => 'application/smil',
3026 'smil' => 'application/smil',
3027 'mif' => 'application/vnd.mif',
3028 'xls' => 'application/vnd.ms-excel',
3029 'ppt' => 'application/vnd.ms-powerpoint',
3030 'wbxml' => 'application/vnd.wap.wbxml',
3031 'wmlc' => 'application/vnd.wap.wmlc',
3032 'dcr' => 'application/x-director',
3033 'dir' => 'application/x-director',
3034 'dxr' => 'application/x-director',
3035 'dvi' => 'application/x-dvi',
3036 'gtar' => 'application/x-gtar',
3037 'php3' => 'application/x-httpd-php',
3038 'php4' => 'application/x-httpd-php',
3039 'php' => 'application/x-httpd-php',
3040 'phtml' => 'application/x-httpd-php',
3041 'phps' => 'application/x-httpd-php-source',
3042 'swf' => 'application/x-shockwave-flash',
3043 'sit' => 'application/x-stuffit',
3044 'tar' => 'application/x-tar',
3045 'tgz' => 'application/x-tar',
3046 'xht' => 'application/xhtml+xml',
3047 'xhtml' => 'application/xhtml+xml',
3048 'zip' => 'application/zip',
3049 'mid' => 'audio/midi',
3050 'midi' => 'audio/midi',
3051 'mp2' => 'audio/mpeg',
3052 'mp3' => 'audio/mpeg',
3053 'mpga' => 'audio/mpeg',
3054 'aif' => 'audio/x-aiff',
3055 'aifc' => 'audio/x-aiff',
3056 'aiff' => 'audio/x-aiff',
3057 'ram' => 'audio/x-pn-realaudio',
3058 'rm' => 'audio/x-pn-realaudio',
3059 'rpm' => 'audio/x-pn-realaudio-plugin',
3060 'ra' => 'audio/x-realaudio',
3061 'wav' => 'audio/x-wav',
3062 'bmp' => 'image/bmp',
3063 'gif' => 'image/gif',
3064 'jpeg' => 'image/jpeg',
3065 'jpe' => 'image/jpeg',
3066 'jpg' => 'image/jpeg',
3067 'png' => 'image/png',
3068 'tiff' => 'image/tiff',
3069 'tif' => 'image/tiff',
3070 'eml' => 'message/rfc822',
3071 'css' => 'text/css',
3072 'html' => 'text/html',
3073 'htm' => 'text/html',
3074 'shtml' => 'text/html',
3075 'log' => 'text/plain',
3076 'text' => 'text/plain',
3077 'txt' => 'text/plain',
3078 'rtx' => 'text/richtext',
3079 'rtf' => 'text/rtf',
3080 'vcf' => 'text/vcard',
3081 'vcard' => 'text/vcard',
3082 'xml' => 'text/xml',
3083 'xsl' => 'text/xml',
3084 'mpeg' => 'video/mpeg',
3085 'mpe' => 'video/mpeg',
3086 'mpg' => 'video/mpeg',
3087 'mov' => 'video/quicktime',
3088 'qt' => 'video/quicktime',
3089 'rv' => 'video/vnd.rn-realvideo',
3090 'avi' => 'video/x-msvideo',
3091 'movie' => 'video/x-sgi-movie'
3092 );
3093 return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
3094 }
3095
3096 /**
3097 * Map a file name to a MIME type.
3098 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3099 * @param string $filename A file name or full path, does not need to exist as a file
3100 * @return string
3101 * @static
3102 */
3103 public static function filenameToType($filename)
3104 {
3105 // In case the path is a URL, strip any query string before getting extension
3106 $qpos = strpos($filename, '?');
3107 if (false !== $qpos) {
3108 $filename = substr($filename, 0, $qpos);
3109 }
3110 $pathinfo = self::mb_pathinfo($filename);
3111 return self::_mime_types($pathinfo['extension']);
3112 }
3113
3114 /**
3115 * Multi-byte-safe pathinfo replacement.
3116 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3117 * Works similarly to the one in PHP >= 5.2.0
3118 * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3119 * @param string $path A filename or path, does not need to exist as a file
3120 * @param integer|string $options Either a PATHINFO_* constant,
3121 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3122 * @return string|array
3123 * @static
3124 */
3125 public static function mb_pathinfo($path, $options = null)
3126 {
3127 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3128 $pathinfo = array();
3129 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3130 if (array_key_exists(1, $pathinfo)) {
3131 $ret['dirname'] = $pathinfo[1];
3132 }
3133 if (array_key_exists(2, $pathinfo)) {
3134 $ret['basename'] = $pathinfo[2];
3135 }
3136 if (array_key_exists(5, $pathinfo)) {
3137 $ret['extension'] = $pathinfo[5];
3138 }
3139 if (array_key_exists(3, $pathinfo)) {
3140 $ret['filename'] = $pathinfo[3];
3141 }
3142 }
3143 switch ($options) {
3144 case PATHINFO_DIRNAME:
3145 case 'dirname':
3146 return $ret['dirname'];
3147 case PATHINFO_BASENAME:
3148 case 'basename':
3149 return $ret['basename'];
3150 case PATHINFO_EXTENSION:
3151 case 'extension':
3152 return $ret['extension'];
3153 case PATHINFO_FILENAME:
3154 case 'filename':
3155 return $ret['filename'];
3156 default:
3157 return $ret;
3158 }
3159 }
3160
3161 /**
3162 * Set or reset instance properties.
3163 * You should avoid this function - it's more verbose, less efficient, more error-prone and
3164 * harder to debug than setting properties directly.
3165 * Usage Example:
3166 * `$mail->set('SMTPSecure', 'tls');`
3167 * is the same as:
3168 * `$mail->SMTPSecure = 'tls';`
3169 * @access public
3170 * @param string $name The property name to set
3171 * @param mixed $value The value to set the property to
3172 * @return boolean
3173 * @TODO Should this not be using the __set() magic function?
3174 */
3175 public function set($name, $value = '')
3176 {
3177 if (property_exists($this, $name)) {
3178 $this->$name = $value;
3179 return true;
3180 } else {
3181 $this->setError($this->lang('variable_set') . $name);
3182 return false;
3183 }
3184 }
3185
3186 /**
3187 * Strip newlines to prevent header injection.
3188 * @access public
3189 * @param string $str
3190 * @return string
3191 */
3192 public function secureHeader($str)
3193 {
3194 return trim(str_replace(array("\r", "\n"), '', $str));
3195 }
3196
3197 /**
3198 * Normalize line breaks in a string.
3199 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3200 * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3201 * @param string $text
3202 * @param string $breaktype What kind of line break to use, defaults to CRLF
3203 * @return string
3204 * @access public
3205 * @static
3206 */
3207 public static function normalizeBreaks($text, $breaktype = "\r\n")
3208 {
3209 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3210 }
3211
3212
3213 /**
3214 * Set the public and private key files and password for S/MIME signing.
3215 * @access public
3216 * @param string $cert_filename
3217 * @param string $key_filename
3218 * @param string $key_pass Password for private key
3219 */
3220 public function sign($cert_filename, $key_filename, $key_pass)
3221 {
3222 $this->sign_cert_file = $cert_filename;
3223 $this->sign_key_file = $key_filename;
3224 $this->sign_key_pass = $key_pass;
3225 }
3226
3227 /**
3228 * Quoted-Printable-encode a DKIM header.
3229 * @access public
3230 * @param string $txt
3231 * @return string
3232 */
3233 public function DKIM_QP($txt)
3234 {
3235 $line = '';
3236 for ($i = 0; $i < strlen($txt); $i++) {
3237 $ord = ord($txt[$i]);
3238 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3239 $line .= $txt[$i];
3240 } else {
3241 $line .= '=' . sprintf('%02X', $ord);
3242 }
3243 }
3244 return $line;
3245 }
3246
3247 /**
3248 * Generate a DKIM signature.
3249 * @access public
3250 * @param string $signHeader
3251 * @throws phpmailerException
3252 * @return string
3253 */
3254 public function DKIM_Sign($signHeader)
3255 {
3256 if (!defined('PKCS7_TEXT')) {
3257 if ($this->exceptions) {
3258 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
3259 }
3260 return '';
3261 }
3262 $privKeyStr = file_get_contents($this->DKIM_private);
3263 if ($this->DKIM_passphrase != '') {
3264 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3265 } else {
3266 $privKey = $privKeyStr;
3267 }
3268 if (openssl_sign($signHeader, $signature, $privKey)) {
3269 return base64_encode($signature);
3270 }
3271 return '';
3272 }
3273
3274 /**
3275 * Generate a DKIM canonicalization header.
3276 * @access public
3277 * @param string $signHeader Header
3278 * @return string
3279 */
3280 public function DKIM_HeaderC($signHeader)
3281 {
3282 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3283 $lines = explode("\r\n", $signHeader);
3284 foreach ($lines as $key => $line) {
3285 list($heading, $value) = explode(':', $line, 2);
3286 $heading = strtolower($heading);
3287 $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
3288 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3289 }
3290 $signHeader = implode("\r\n", $lines);
3291 return $signHeader;
3292 }
3293
3294 /**
3295 * Generate a DKIM canonicalization body.
3296 * @access public
3297 * @param string $body Message Body
3298 * @return string
3299 */
3300 public function DKIM_BodyC($body)
3301 {
3302 if ($body == '') {
3303 return "\r\n";
3304 }
3305 // stabilize line endings
3306 $body = str_replace("\r\n", "\n", $body);
3307 $body = str_replace("\n", "\r\n", $body);
3308 // END stabilize line endings
3309 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3310 $body = substr($body, 0, strlen($body) - 2);
3311 }
3312 return $body;
3313 }
3314
3315 /**
3316 * Create the DKIM header and body in a new message header.
3317 * @access public
3318 * @param string $headers_line Header lines
3319 * @param string $subject Subject
3320 * @param string $body Body
3321 * @return string
3322 */
3323 public function DKIM_Add($headers_line, $subject, $body)
3324 {
3325 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3326 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3327 $DKIMquery = 'dns/txt'; // Query method
3328 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3329 $subject_header = "Subject: $subject";
3330 $headers = explode($this->LE, $headers_line);
3331 $from_header = '';
3332 $to_header = '';
3333 $current = '';
3334 foreach ($headers as $header) {
3335 if (strpos($header, 'From:') === 0) {
3336 $from_header = $header;
3337 $current = 'from_header';
3338 } elseif (strpos($header, 'To:') === 0) {
3339 $to_header = $header;
3340 $current = 'to_header';
3341 } else {
3342 if ($current && strpos($header, ' =?') === 0) {
3343 $current .= $header;
3344 } else {
3345 $current = '';
3346 }
3347 }
3348 }
3349 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3350 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3351 $subject = str_replace(
3352 '|',
3353 '=7C',
3354 $this->DKIM_QP($subject_header)
3355 ); // Copied header fields (dkim-quoted-printable)
3356 $body = $this->DKIM_BodyC($body);
3357 $DKIMlen = strlen($body); // Length of body
3358 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3359 $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
3360 $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3361 $DKIMsignatureType . '; q=' .
3362 $DKIMquery . '; l=' .
3363 $DKIMlen . '; s=' .
3364 $this->DKIM_selector .
3365 ";\r\n" .
3366 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3367 "\th=From:To:Subject;\r\n" .
3368 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3369 "\tz=$from\r\n" .
3370 "\t|$to\r\n" .
3371 "\t|$subject;\r\n" .
3372 "\tbh=" . $DKIMb64 . ";\r\n" .
3373 "\tb=";
3374 $toSign = $this->DKIM_HeaderC(
3375 $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
3376 );
3377 $signed = $this->DKIM_Sign($toSign);
3378 return $dkimhdrs . $signed . "\r\n";
3379 }
3380
3381 /**
3382 * Allows for public read access to 'to' property.
3383 * @access public
3384 * @return array
3385 */
3386 public function getToAddresses()
3387 {
3388 return $this->to;
3389 }
3390
3391 /**
3392 * Allows for public read access to 'cc' property.
3393 * @access public
3394 * @return array
3395 */
3396 public function getCcAddresses()
3397 {
3398 return $this->cc;
3399 }
3400
3401 /**
3402 * Allows for public read access to 'bcc' property.
3403 * @access public
3404 * @return array
3405 */
3406 public function getBccAddresses()
3407 {
3408 return $this->bcc;
3409 }
3410
3411 /**
3412 * Allows for public read access to 'ReplyTo' property.
3413 * @access public
3414 * @return array
3415 */
3416 public function getReplyToAddresses()
3417 {
3418 return $this->ReplyTo;
3419 }
3420
3421 /**
3422 * Allows for public read access to 'all_recipients' property.
3423 * @access public
3424 * @return array
3425 */
3426 public function getAllRecipientAddresses()
3427 {
3428 return $this->all_recipients;
3429 }
3430
3431 /**
3432 * Perform a callback.
3433 * @param boolean $isSent
3434 * @param array $to
3435 * @param array $cc
3436 * @param array $bcc
3437 * @param string $subject
3438 * @param string $body
3439 * @param string $from
3440 */
3441 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3442 {
3443 if (!empty($this->action_function) && is_callable($this->action_function)) {
3444 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3445 call_user_func_array($this->action_function, $params);
3446 }
3447 }
3448 }
3449
3450 /**
3451 * PHPMailer exception handler
3452 * @package PHPMailer
3453 */
3454 class phpmailerException extends Exception
3455 {
3456 /**
3457 * Prettify error message output
3458 * @return string
3459 */
3460 public function errorMessage()
3461 {
3462 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3463 return $errorMsg;
3464 }
3465 }
2 /**
3 * PHPMailer - PHP email creation and transport class.
4 * PHP Version 5
5 * @package PHPMailer
6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10 * @author Brent R. Matzelle (original founder)
11 * @copyright 2012 - 2014 Marcus Bointon
12 * @copyright 2010 - 2012 Jim Jagielski
13 * @copyright 2004 - 2009 Andy Prevost
14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15 * @note This program is distributed in the hope that it will be useful - WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 /**
21 * PHPMailer - PHP email creation and transport class.
22 * @package PHPMailer
23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
24 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
25 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
26 * @author Brent R. Matzelle (original founder)
27 */
28 class PHPMailer
29 {
30 /**
31 * The PHPMailer Version number.
32 * @type string
33 */
34 public $Version = '5.2.9';
35
36 /**
37 * Email priority.
38 * Options: 1 = High, 3 = Normal, 5 = low.
39 * @type integer
40 */
41 public $Priority = 3;
42
43 /**
44 * The character set of the message.
45 * @type string
46 */
47 public $CharSet = 'iso-8859-1';
48
49 /**
50 * The MIME Content-type of the message.
51 * @type string
52 */
53 public $ContentType = 'text/plain';
54
55 /**
56 * The message encoding.
57 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
58 * @type string
59 */
60 public $Encoding = '8bit';
61
62 /**
63 * Holds the most recent mailer error message.
64 * @type string
65 */
66 public $ErrorInfo = '';
67
68 /**
69 * The From email address for the message.
70 * @type string
71 */
72 public $From = 'root@localhost';
73
74 /**
75 * The From name of the message.
76 * @type string
77 */
78 public $FromName = 'Root User';
79
80 /**
81 * The Sender email (Return-Path) of the message.
82 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
83 * @type string
84 */
85 public $Sender = '';
86
87 /**
88 * The Return-Path of the message.
89 * If empty, it will be set to either From or Sender.
90 * @type string
91 * @deprecated Email senders should never set a return-path header;
92 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
93 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
94 */
95 public $ReturnPath = '';
96
97 /**
98 * The Subject of the message.
99 * @type string
100 */
101 public $Subject = '';
102
103 /**
104 * An HTML or plain text message body.
105 * If HTML then call isHTML(true).
106 * @type string
107 */
108 public $Body = '';
109
110 /**
111 * The plain-text message body.
112 * This body can be read by mail clients that do not have HTML email
113 * capability such as mutt & Eudora.
114 * Clients that can read HTML will view the normal Body.
115 * @type string
116 */
117 public $AltBody = '';
118
119 /**
120 * An iCal message part body.
121 * Only supported in simple alt or alt_inline message types
122 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
123 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
124 * @link http://kigkonsult.se/iCalcreator/
125 * @type string
126 */
127 public $Ical = '';
128
129 /**
130 * The complete compiled MIME message body.
131 * @access protected
132 * @type string
133 */
134 protected $MIMEBody = '';
135
136 /**
137 * The complete compiled MIME message headers.
138 * @type string
139 * @access protected
140 */
141 protected $MIMEHeader = '';
142
143 /**
144 * Extra headers that createHeader() doesn't fold in.
145 * @type string
146 * @access protected
147 */
148 protected $mailHeader = '';
149
150 /**
151 * Word-wrap the message body to this number of chars.
152 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
153 * @type integer
154 */
155 public $WordWrap = 0;
156
157 /**
158 * Which method to use to send mail.
159 * Options: "mail", "sendmail", or "smtp".
160 * @type string
161 */
162 public $Mailer = 'mail';
163
164 /**
165 * The path to the sendmail program.
166 * @type string
167 */
168 public $Sendmail = '/usr/sbin/sendmail';
169
170 /**
171 * Whether mail() uses a fully sendmail-compatible MTA.
172 * One which supports sendmail's "-oi -f" options.
173 * @type boolean
174 */
175 public $UseSendmailOptions = true;
176
177 /**
178 * Path to PHPMailer plugins.
179 * Useful if the SMTP class is not in the PHP include path.
180 * @type string
181 * @deprecated Should not be needed now there is an autoloader.
182 */
183 public $PluginDir = '';
184
185 /**
186 * The email address that a reading confirmation should be sent to.
187 * @type string
188 */
189 public $ConfirmReadingTo = '';
190
191 /**
192 * The hostname to use in Message-Id and Received headers
193 * and as default HELO string.
194 * If empty, the value returned
195 * by SERVER_NAME is used or 'localhost.localdomain'.
196 * @type string
197 */
198 public $Hostname = '';
199
200 /**
201 * An ID to be used in the Message-Id header.
202 * If empty, a unique id will be generated.
203 * @type string
204 */
205 public $MessageID = '';
206
207 /**
208 * The message Date to be used in the Date header.
209 * If empty, the current date will be added.
210 * @type string
211 */
212 public $MessageDate = '';
213
214 /**
215 * SMTP hosts.
216 * Either a single hostname or multiple semicolon-delimited hostnames.
217 * You can also specify a different port
218 * for each host by using this format: [hostname:port]
219 * (e.g. "smtp1.example.com:25;smtp2.example.com").
220 * You can also specify encryption type, for example:
221 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
222 * Hosts will be tried in order.
223 * @type string
224 */
225 public $Host = 'localhost';
226
227 /**
228 * The default SMTP server port.
229 * @type integer
230 * @TODO Why is this needed when the SMTP class takes care of it?
231 */
232 public $Port = 25;
233
234 /**
235 * The SMTP HELO of the message.
236 * Default is $Hostname.
237 * @type string
238 * @see PHPMailer::$Hostname
239 */
240 public $Helo = '';
241
242 /**
243 * The secure connection prefix.
244 * Options: "", "ssl" or "tls"
245 * @type string
246 */
247 public $SMTPSecure = '';
248
249 /**
250 * Whether to use SMTP authentication.
251 * Uses the Username and Password properties.
252 * @type boolean
253 * @see PHPMailer::$Username
254 * @see PHPMailer::$Password
255 */
256 public $SMTPAuth = false;
257
258 /**
259 * SMTP username.
260 * @type string
261 */
262 public $Username = '';
263
264 /**
265 * SMTP password.
266 * @type string
267 */
268 public $Password = '';
269
270 /**
271 * SMTP auth type.
272 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
273 * @type string
274 */
275 public $AuthType = '';
276
277 /**
278 * SMTP realm.
279 * Used for NTLM auth
280 * @type string
281 */
282 public $Realm = '';
283
284 /**
285 * SMTP workstation.
286 * Used for NTLM auth
287 * @type string
288 */
289 public $Workstation = '';
290
291 /**
292 * The SMTP server timeout in seconds.
293 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
294 * @type integer
295 */
296 public $Timeout = 300;
297
298 /**
299 * SMTP class debug output mode.
300 * Debug output level.
301 * Options:
302 * * `0` No output
303 * * `1` Commands
304 * * `2` Data and commands
305 * * `3` As 2 plus connection status
306 * * `4` Low-level data output
307 * @type integer
308 * @see SMTP::$do_debug
309 */
310 public $SMTPDebug = 0;
311
312 /**
313 * How to handle debug output.
314 * Options:
315 * * `echo` Output plain-text as-is, appropriate for CLI
316 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
317 * * `error_log` Output to error log as configured in php.ini
318 *
319 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
320 * <code>
321 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
322 * </code>
323 * @type string|callable
324 * @see SMTP::$Debugoutput
325 */
326 public $Debugoutput = 'echo';
327
328 /**
329 * Whether to keep SMTP connection open after each message.
330 * If this is set to true then to close the connection
331 * requires an explicit call to smtpClose().
332 * @type boolean
333 */
334 public $SMTPKeepAlive = false;
335
336 /**
337 * Whether to split multiple to addresses into multiple messages
338 * or send them all in one message.
339 * @type boolean
340 */
341 public $SingleTo = false;
342
343 /**
344 * Storage for addresses when SingleTo is enabled.
345 * @type array
346 * @TODO This should really not be public
347 */
348 public $SingleToArray = array();
349
350 /**
351 * Whether to generate VERP addresses on send.
352 * Only applicable when sending via SMTP.
353 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
354 * @link http://www.postfix.org/VERP_README.html Postfix VERP info
355 * @type boolean
356 */
357 public $do_verp = false;
358
359 /**
360 * Whether to allow sending messages with an empty body.
361 * @type boolean
362 */
363 public $AllowEmpty = false;
364
365 /**
366 * The default line ending.
367 * @note The default remains "\n". We force CRLF where we know
368 * it must be used via self::CRLF.
369 * @type string
370 */
371 public $LE = "\n";
372
373 /**
374 * DKIM selector.
375 * @type string
376 */
377 public $DKIM_selector = '';
378
379 /**
380 * DKIM Identity.
381 * Usually the email address used as the source of the email
382 * @type string
383 */
384 public $DKIM_identity = '';
385
386 /**
387 * DKIM passphrase.
388 * Used if your key is encrypted.
389 * @type string
390 */
391 public $DKIM_passphrase = '';
392
393 /**
394 * DKIM signing domain name.
395 * @example 'example.com'
396 * @type string
397 */
398 public $DKIM_domain = '';
399
400 /**
401 * DKIM private key file path.
402 * @type string
403 */
404 public $DKIM_private = '';
405
406 /**
407 * Callback Action function name.
408 *
409 * The function that handles the result of the send email action.
410 * It is called out by send() for each email sent.
411 *
412 * Value can be any php callable: http://www.php.net/is_callable
413 *
414 * Parameters:
415 * boolean $result result of the send action
416 * string $to email address of the recipient
417 * string $cc cc email addresses
418 * string $bcc bcc email addresses
419 * string $subject the subject
420 * string $body the email body
421 * string $from email address of sender
422 * @type string
423 */
424 public $action_function = '';
425
426 /**
427 * What to put in the X-Mailer header.
428 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
429 * @type string
430 */
431 public $XMailer = '';
432
433 /**
434 * An instance of the SMTP sender class.
435 * @type SMTP
436 * @access protected
437 */
438 protected $smtp = null;
439
440 /**
441 * The array of 'to' addresses.
442 * @type array
443 * @access protected
444 */
445 protected $to = array();
446
447 /**
448 * The array of 'cc' addresses.
449 * @type array
450 * @access protected
451 */
452 protected $cc = array();
453
454 /**
455 * The array of 'bcc' addresses.
456 * @type array
457 * @access protected
458 */
459 protected $bcc = array();
460
461 /**
462 * The array of reply-to names and addresses.
463 * @type array
464 * @access protected
465 */
466 protected $ReplyTo = array();
467
468 /**
469 * An array of all kinds of addresses.
470 * Includes all of $to, $cc, $bcc, $replyto
471 * @type array
472 * @access protected
473 */
474 protected $all_recipients = array();
475
476 /**
477 * The array of attachments.
478 * @type array
479 * @access protected
480 */
481 protected $attachment = array();
482
483 /**
484 * The array of custom headers.
485 * @type array
486 * @access protected
487 */
488 protected $CustomHeader = array();
489
490 /**
491 * The most recent Message-ID (including angular brackets).
492 * @type string
493 * @access protected
494 */
495 protected $lastMessageID = '';
496
497 /**
498 * The message's MIME type.
499 * @type string
500 * @access protected
501 */
502 protected $message_type = '';
503
504 /**
505 * The array of MIME boundary strings.
506 * @type array
507 * @access protected
508 */
509 protected $boundary = array();
510
511 /**
512 * The array of available languages.
513 * @type array
514 * @access protected
515 */
516 protected $language = array();
517
518 /**
519 * The number of errors encountered.
520 * @type integer
521 * @access protected
522 */
523 protected $error_count = 0;
524
525 /**
526 * The S/MIME certificate file path.
527 * @type string
528 * @access protected
529 */
530 protected $sign_cert_file = '';
531
532 /**
533 * The S/MIME key file path.
534 * @type string
535 * @access protected
536 */
537 protected $sign_key_file = '';
538
539 /**
540 * The S/MIME password for the key.
541 * Used only if the key is encrypted.
542 * @type string
543 * @access protected
544 */
545 protected $sign_key_pass = '';
546
547 /**
548 * Whether to throw exceptions for errors.
549 * @type boolean
550 * @access protected
551 */
552 protected $exceptions = false;
553
554 /**
555 * Error severity: message only, continue processing.
556 */
557 const STOP_MESSAGE = 0;
558
559 /**
560 * Error severity: message, likely ok to continue processing.
561 */
562 const STOP_CONTINUE = 1;
563
564 /**
565 * Error severity: message, plus full stop, critical error reached.
566 */
567 const STOP_CRITICAL = 2;
568
569 /**
570 * SMTP RFC standard line ending.
571 */
572 const CRLF = "\r\n";
573
574 /**
575 * Constructor.
576 * @param boolean $exceptions Should we throw external exceptions?
577 */
578 public function __construct($exceptions = false)
579 {
580 $this->exceptions = (boolean)$exceptions;
581 }
582
583 /**
584 * Destructor.
585 */
586 public function __destruct()
587 {
588 if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
589 $this->smtpClose();
590 }
591 }
592
593 /**
594 * Call mail() in a safe_mode-aware fashion.
595 * Also, unless sendmail_path points to sendmail (or something that
596 * claims to be sendmail), don't pass params (not a perfect fix,
597 * but it will do)
598 * @param string $to To
599 * @param string $subject Subject
600 * @param string $body Message Body
601 * @param string $header Additional Header(s)
602 * @param string $params Params
603 * @access private
604 * @return boolean
605 */
606 private function mailPassthru($to, $subject, $body, $header, $params)
607 {
608 //Check overloading of mail function to avoid double-encoding
609 if (ini_get('mbstring.func_overload') & 1) {
610 $subject = $this->secureHeader($subject);
611 } else {
612 $subject = $this->encodeHeader($this->secureHeader($subject));
613 }
614 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
615 $result = @mail($to, $subject, $body, $header);
616 } else {
617 $result = @mail($to, $subject, $body, $header, $params);
618 }
619 return $result;
620 }
621
622 /**
623 * Output debugging info via user-defined method.
624 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
625 * @see PHPMailer::$Debugoutput
626 * @see PHPMailer::$SMTPDebug
627 * @param string $str
628 */
629 protected function edebug($str)
630 {
631 if ($this->SMTPDebug <= 0) {
632 return;
633 }
634 //Avoid clash with built-in function names
635 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
636 call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
637 return;
638 }
639 switch ($this->Debugoutput) {
640 case 'error_log':
641 //Don't output, just log
642 error_log($str);
643 break;
644 case 'html':
645 //Cleans up output a bit for a better looking, HTML-safe output
646 echo htmlentities(
647 preg_replace('/[\r\n]+/', '', $str),
648 ENT_QUOTES,
649 'UTF-8'
650 )
651 . "<br>\n";
652 break;
653 case 'echo':
654 default:
655 //Normalize line breaks
656 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
657 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
658 "\n",
659 "\n \t ",
660 trim($str)
661 ) . "\n";
662 }
663 }
664
665 /**
666 * Sets message type to HTML or plain.
667 * @param boolean $isHtml True for HTML mode.
668 * @return void
669 */
670 public function isHTML($isHtml = true)
671 {
672 if ($isHtml) {
673 $this->ContentType = 'text/html';
674 } else {
675 $this->ContentType = 'text/plain';
676 }
677 }
678
679 /**
680 * Send messages using SMTP.
681 * @return void
682 */
683 public function isSMTP()
684 {
685 $this->Mailer = 'smtp';
686 }
687
688 /**
689 * Send messages using PHP's mail() function.
690 * @return void
691 */
692 public function isMail()
693 {
694 $this->Mailer = 'mail';
695 }
696
697 /**
698 * Send messages using $Sendmail.
699 * @return void
700 */
701 public function isSendmail()
702 {
703 $ini_sendmail_path = ini_get('sendmail_path');
704
705 if (!stristr($ini_sendmail_path, 'sendmail')) {
706 $this->Sendmail = '/usr/sbin/sendmail';
707 } else {
708 $this->Sendmail = $ini_sendmail_path;
709 }
710 $this->Mailer = 'sendmail';
711 }
712
713 /**
714 * Send messages using qmail.
715 * @return void
716 */
717 public function isQmail()
718 {
719 $ini_sendmail_path = ini_get('sendmail_path');
720
721 if (!stristr($ini_sendmail_path, 'qmail')) {
722 $this->Sendmail = '/var/qmail/bin/qmail-inject';
723 } else {
724 $this->Sendmail = $ini_sendmail_path;
725 }
726 $this->Mailer = 'qmail';
727 }
728
729 /**
730 * Add a "To" address.
731 * @param string $address
732 * @param string $name
733 * @return boolean true on success, false if address already used
734 */
735 public function addAddress($address, $name = '')
736 {
737 return $this->addAnAddress('to', $address, $name);
738 }
739
740 /**
741 * Add a "CC" address.
742 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
743 * @param string $address
744 * @param string $name
745 * @return boolean true on success, false if address already used
746 */
747 public function addCC($address, $name = '')
748 {
749 return $this->addAnAddress('cc', $address, $name);
750 }
751
752 /**
753 * Add a "BCC" address.
754 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
755 * @param string $address
756 * @param string $name
757 * @return boolean true on success, false if address already used
758 */
759 public function addBCC($address, $name = '')
760 {
761 return $this->addAnAddress('bcc', $address, $name);
762 }
763
764 /**
765 * Add a "Reply-to" address.
766 * @param string $address
767 * @param string $name
768 * @return boolean
769 */
770 public function addReplyTo($address, $name = '')
771 {
772 return $this->addAnAddress('Reply-To', $address, $name);
773 }
774
775 /**
776 * Add an address to one of the recipient arrays.
777 * Addresses that have been added already return false, but do not throw exceptions
778 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
779 * @param string $address The email address to send to
780 * @param string $name
781 * @throws phpmailerException
782 * @return boolean true on success, false if address already used or invalid in some way
783 * @access protected
784 */
785 protected function addAnAddress($kind, $address, $name = '')
786 {
787 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
788 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
789 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
790 if ($this->exceptions) {
791 throw new phpmailerException('Invalid recipient array: ' . $kind);
792 }
793 return false;
794 }
795 $address = trim($address);
796 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
797 if (!$this->validateAddress($address)) {
798 $this->setError($this->lang('invalid_address') . ': ' . $address);
799 $this->edebug($this->lang('invalid_address') . ': ' . $address);
800 if ($this->exceptions) {
801 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
802 }
803 return false;
804 }
805 if ($kind != 'Reply-To') {
806 if (!isset($this->all_recipients[strtolower($address)])) {
807 array_push($this->$kind, array($address, $name));
808 $this->all_recipients[strtolower($address)] = true;
809 return true;
810 }
811 } else {
812 if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
813 $this->ReplyTo[strtolower($address)] = array($address, $name);
814 return true;
815 }
816 }
817 return false;
818 }
819
820 /**
821 * Set the From and FromName properties.
822 * @param string $address
823 * @param string $name
824 * @param boolean $auto Whether to also set the Sender address, defaults to true
825 * @throws phpmailerException
826 * @return boolean
827 */
828 public function setFrom($address, $name = '', $auto = true)
829 {
830 $address = trim($address);
831 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
832 if (!$this->validateAddress($address)) {
833 $this->setError($this->lang('invalid_address') . ': ' . $address);
834 $this->edebug($this->lang('invalid_address') . ': ' . $address);
835 if ($this->exceptions) {
836 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
837 }
838 return false;
839 }
840 $this->From = $address;
841 $this->FromName = $name;
842 if ($auto) {
843 if (empty($this->Sender)) {
844 $this->Sender = $address;
845 }
846 }
847 return true;
848 }
849
850 /**
851 * Return the Message-ID header of the last email.
852 * Technically this is the value from the last time the headers were created,
853 * but it's also the message ID of the last sent message except in
854 * pathological cases.
855 * @return string
856 */
857 public function getLastMessageID()
858 {
859 return $this->lastMessageID;
860 }
861
862 /**
863 * Check that a string looks like an email address.
864 * @param string $address The email address to check
865 * @param string $patternselect A selector for the validation pattern to use :
866 * * `auto` Pick strictest one automatically;
867 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
868 * * `pcre` Use old PCRE implementation;
869 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
870 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
871 * * `noregex` Don't use a regex: super fast, really dumb.
872 * @return boolean
873 * @static
874 * @access public
875 */
876 public static function validateAddress($address, $patternselect = 'auto')
877 {
878 if (!$patternselect or $patternselect == 'auto') {
879 //Check this constant first so it works when extension_loaded() is disabled by safe mode
880 //Constant was added in PHP 5.2.4
881 if (defined('PCRE_VERSION')) {
882 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
883 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
884 $patternselect = 'pcre8';
885 } else {
886 $patternselect = 'pcre';
887 }
888 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
889 //Fall back to older PCRE
890 $patternselect = 'pcre';
891 } else {
892 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
893 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
894 $patternselect = 'php';
895 } else {
896 $patternselect = 'noregex';
897 }
898 }
899 }
900 switch ($patternselect) {
901 case 'pcre8':
902 /**
903 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
904 * @link http://squiloople.com/2009/12/20/email-address-validation/
905 * @copyright 2009-2010 Michael Rushton
906 * Feel free to use and redistribute this code. But please keep this copyright notice.
907 */
908 return (boolean)preg_match(
909 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
910 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
911 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
912 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
913 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
914 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
915 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
916 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
917 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
918 $address
919 );
920 case 'pcre':
921 //An older regex that doesn't need a recent PCRE
922 return (boolean)preg_match(
923 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
924 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
925 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
926 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
927 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
928 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
929 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
930 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
931 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
932 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
933 $address
934 );
935 case 'html5':
936 /**
937 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
938 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
939 */
940 return (boolean)preg_match(
941 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
942 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
943 $address
944 );
945 case 'noregex':
946 //No PCRE! Do something _very_ approximate!
947 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
948 return (strlen($address) >= 3
949 and strpos($address, '@') >= 1
950 and strpos($address, '@') != strlen($address) - 1);
951 case 'php':
952 default:
953 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
954 }
955 }
956
957 /**
958 * Create a message and send it.
959 * Uses the sending method specified by $Mailer.
960 * @throws phpmailerException
961 * @return boolean false on error - See the ErrorInfo property for details of the error.
962 */
963 public function send()
964 {
965 try {
966 if (!$this->preSend()) {
967 return false;
968 }
969 return $this->postSend();
970 } catch (phpmailerException $exc) {
971 $this->mailHeader = '';
972 $this->setError($exc->getMessage());
973 if ($this->exceptions) {
974 throw $exc;
975 }
976 return false;
977 }
978 }
979
980 /**
981 * Prepare a message for sending.
982 * @throws phpmailerException
983 * @return boolean
984 */
985 public function preSend()
986 {
987 try {
988 $this->mailHeader = '';
989 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
990 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
991 }
992
993 // Set whether the message is multipart/alternative
994 if (!empty($this->AltBody)) {
995 $this->ContentType = 'multipart/alternative';
996 }
997
998 $this->error_count = 0; // reset errors
999 $this->setMessageType();
1000 // Refuse to send an empty message unless we are specifically allowing it
1001 if (!$this->AllowEmpty and empty($this->Body)) {
1002 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1003 }
1004
1005 $this->MIMEHeader = $this->createHeader();
1006 $this->MIMEBody = $this->createBody();
1007
1008 // To capture the complete message when using mail(), create
1009 // an extra header list which createHeader() doesn't fold in
1010 if ($this->Mailer == 'mail') {
1011 if (count($this->to) > 0) {
1012 $this->mailHeader .= $this->addrAppend('To', $this->to);
1013 } else {
1014 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1015 }
1016 $this->mailHeader .= $this->headerLine(
1017 'Subject',
1018 $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1019 );
1020 }
1021
1022 // Sign with DKIM if enabled
1023 if (!empty($this->DKIM_domain)
1024 && !empty($this->DKIM_private)
1025 && !empty($this->DKIM_selector)
1026 && file_exists($this->DKIM_private)) {
1027 $header_dkim = $this->DKIM_Add(
1028 $this->MIMEHeader . $this->mailHeader,
1029 $this->encodeHeader($this->secureHeader($this->Subject)),
1030 $this->MIMEBody
1031 );
1032 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1033 str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1034 }
1035 return true;
1036
1037 } catch (phpmailerException $exc) {
1038 $this->setError($exc->getMessage());
1039 if ($this->exceptions) {
1040 throw $exc;
1041 }
1042 return false;
1043 }
1044 }
1045
1046 /**
1047 * Actually send a message.
1048 * Send the email via the selected mechanism
1049 * @throws phpmailerException
1050 * @return boolean
1051 */
1052 public function postSend()
1053 {
1054 try {
1055 // Choose the mailer and send through it
1056 switch ($this->Mailer) {
1057 case 'sendmail':
1058 case 'qmail':
1059 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1060 case 'smtp':
1061 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1062 case 'mail':
1063 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1064 default:
1065 $sendMethod = $this->Mailer.'Send';
1066 if (method_exists($this, $sendMethod)) {
1067 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1068 }
1069
1070 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1071 }
1072 } catch (phpmailerException $exc) {
1073 $this->setError($exc->getMessage());
1074 $this->edebug($exc->getMessage());
1075 if ($this->exceptions) {
1076 throw $exc;
1077 }
1078 }
1079 return false;
1080 }
1081
1082 /**
1083 * Send mail using the $Sendmail program.
1084 * @param string $header The message headers
1085 * @param string $body The message body
1086 * @see PHPMailer::$Sendmail
1087 * @throws phpmailerException
1088 * @access protected
1089 * @return boolean
1090 */
1091 protected function sendmailSend($header, $body)
1092 {
1093 if ($this->Sender != '') {
1094 if ($this->Mailer == 'qmail') {
1095 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1096 } else {
1097 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1098 }
1099 } else {
1100 if ($this->Mailer == 'qmail') {
1101 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1102 } else {
1103 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1104 }
1105 }
1106 if ($this->SingleTo) {
1107 foreach ($this->SingleToArray as $toAddr) {
1108 if (!@$mail = popen($sendmail, 'w')) {
1109 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1110 }
1111 fputs($mail, 'To: ' . $toAddr . "\n");
1112 fputs($mail, $header);
1113 fputs($mail, $body);
1114 $result = pclose($mail);
1115 $this->doCallback(
1116 ($result == 0),
1117 array($toAddr),
1118 $this->cc,
1119 $this->bcc,
1120 $this->Subject,
1121 $body,
1122 $this->From
1123 );
1124 if ($result != 0) {
1125 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1126 }
1127 }
1128 } else {
1129 if (!@$mail = popen($sendmail, 'w')) {
1130 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1131 }
1132 fputs($mail, $header);
1133 fputs($mail, $body);
1134 $result = pclose($mail);
1135 $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1136 if ($result != 0) {
1137 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1138 }
1139 }
1140 return true;
1141 }
1142
1143 /**
1144 * Send mail using the PHP mail() function.
1145 * @param string $header The message headers
1146 * @param string $body The message body
1147 * @link http://www.php.net/manual/en/book.mail.php
1148 * @throws phpmailerException
1149 * @access protected
1150 * @return boolean
1151 */
1152 protected function mailSend($header, $body)
1153 {
1154 $toArr = array();
1155 foreach ($this->to as $toaddr) {
1156 $toArr[] = $this->addrFormat($toaddr);
1157 }
1158 $to = implode(', ', $toArr);
1159
1160 if (empty($this->Sender)) {
1161 $params = ' ';
1162 } else {
1163 $params = sprintf('-f%s', $this->Sender);
1164 }
1165 if ($this->Sender != '' and !ini_get('safe_mode')) {
1166 $old_from = ini_get('sendmail_from');
1167 ini_set('sendmail_from', $this->Sender);
1168 }
1169 $result = false;
1170 if ($this->SingleTo && count($toArr) > 1) {
1171 foreach ($toArr as $toAddr) {
1172 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1173 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1174 }
1175 } else {
1176 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1177 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1178 }
1179 if (isset($old_from)) {
1180 ini_set('sendmail_from', $old_from);
1181 }
1182 if (!$result) {
1183 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1184 }
1185 return true;
1186 }
1187
1188 /**
1189 * Get an instance to use for SMTP operations.
1190 * Override this function to load your own SMTP implementation
1191 * @return SMTP
1192 */
1193 public function getSMTPInstance()
1194 {
1195 if (!is_object($this->smtp)) {
1196 $this->smtp = new SMTP;
1197 }
1198 return $this->smtp;
1199 }
1200
1201 /**
1202 * Send mail via SMTP.
1203 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1204 * Uses the PHPMailerSMTP class by default.
1205 * @see PHPMailer::getSMTPInstance() to use a different class.
1206 * @param string $header The message headers
1207 * @param string $body The message body
1208 * @throws phpmailerException
1209 * @uses SMTP
1210 * @access protected
1211 * @return boolean
1212 */
1213 protected function smtpSend($header, $body)
1214 {
1215 $bad_rcpt = array();
1216
1217 if (!$this->smtpConnect()) {
1218 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1219 }
1220 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1221 if (!$this->smtp->mail($smtp_from)) {
1222 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1223 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1224 }
1225
1226 // Attempt to send to all recipients
1227 foreach ($this->to as $to) {
1228 if (!$this->smtp->recipient($to[0])) {
1229 $bad_rcpt[] = $to[0];
1230 $isSent = false;
1231 } else {
1232 $isSent = true;
1233 }
1234 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1235 }
1236 foreach ($this->cc as $cc) {
1237 if (!$this->smtp->recipient($cc[0])) {
1238 $bad_rcpt[] = $cc[0];
1239 $isSent = false;
1240 } else {
1241 $isSent = true;
1242 }
1243 $this->doCallback($isSent, array(), array($cc[0]), array(), $this->Subject, $body, $this->From);
1244 }
1245 foreach ($this->bcc as $bcc) {
1246 if (!$this->smtp->recipient($bcc[0])) {
1247 $bad_rcpt[] = $bcc[0];
1248 $isSent = false;
1249 } else {
1250 $isSent = true;
1251 }
1252 $this->doCallback($isSent, array(), array(), array($bcc[0]), $this->Subject, $body, $this->From);
1253 }
1254
1255 // Only send the DATA command if we have viable recipients
1256 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1257 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1258 }
1259 if ($this->SMTPKeepAlive) {
1260 $this->smtp->reset();
1261 } else {
1262 $this->smtp->quit();
1263 $this->smtp->close();
1264 }
1265 if (count($bad_rcpt) > 0) { // Create error message for any bad addresses
1266 throw new phpmailerException(
1267 $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
1268 self::STOP_CONTINUE
1269 );
1270 }
1271 return true;
1272 }
1273
1274 /**
1275 * Initiate a connection to an SMTP server.
1276 * Returns false if the operation failed.
1277 * @param array $options An array of options compatible with stream_context_create()
1278 * @uses SMTP
1279 * @access public
1280 * @throws phpmailerException
1281 * @return boolean
1282 */
1283 public function smtpConnect($options = array())
1284 {
1285 if (is_null($this->smtp)) {
1286 $this->smtp = $this->getSMTPInstance();
1287 }
1288
1289 // Already connected?
1290 if ($this->smtp->connected()) {
1291 return true;
1292 }
1293
1294 $this->smtp->setTimeout($this->Timeout);
1295 $this->smtp->setDebugLevel($this->SMTPDebug);
1296 $this->smtp->setDebugOutput($this->Debugoutput);
1297 $this->smtp->setVerp($this->do_verp);
1298 $hosts = explode(';', $this->Host);
1299 $lastexception = null;
1300
1301 foreach ($hosts as $hostentry) {
1302 $hostinfo = array();
1303 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1304 // Not a valid host entry
1305 continue;
1306 }
1307 // $hostinfo[2]: optional ssl or tls prefix
1308 // $hostinfo[3]: the hostname
1309 // $hostinfo[4]: optional port number
1310 // The host string prefix can temporarily override the current setting for SMTPSecure
1311 // If it's not specified, the default value is used
1312 $prefix = '';
1313 $tls = ($this->SMTPSecure == 'tls');
1314 if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
1315 $prefix = 'ssl://';
1316 $tls = false; // Can't have SSL and TLS at once
1317 } elseif ($hostinfo[2] == 'tls') {
1318 $tls = true;
1319 // tls doesn't use a prefix
1320 }
1321 $host = $hostinfo[3];
1322 $port = $this->Port;
1323 $tport = (integer)$hostinfo[4];
1324 if ($tport > 0 and $tport < 65536) {
1325 $port = $tport;
1326 }
1327 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1328 try {
1329 if ($this->Helo) {
1330 $hello = $this->Helo;
1331 } else {
1332 $hello = $this->serverHostname();
1333 }
1334 $this->smtp->hello($hello);
1335
1336 if ($tls) {
1337 if (!$this->smtp->startTLS()) {
1338 throw new phpmailerException($this->lang('connect_host'));
1339 }
1340 // We must resend HELO after tls negotiation
1341 $this->smtp->hello($hello);
1342 }
1343 if ($this->SMTPAuth) {
1344 if (!$this->smtp->authenticate(
1345 $this->Username,
1346 $this->Password,
1347 $this->AuthType,
1348 $this->Realm,
1349 $this->Workstation
1350 )
1351 ) {
1352 throw new phpmailerException($this->lang('authenticate'));
1353 }
1354 }
1355 return true;
1356 } catch (phpmailerException $exc) {
1357 $lastexception = $exc;
1358 // We must have connected, but then failed TLS or Auth, so close connection nicely
1359 $this->smtp->quit();
1360 }
1361 }
1362 }
1363 // If we get here, all connection attempts have failed, so close connection hard
1364 $this->smtp->close();
1365 // As we've caught all exceptions, just report whatever the last one was
1366 if ($this->exceptions and !is_null($lastexception)) {
1367 throw $lastexception;
1368 }
1369 return false;
1370 }
1371
1372 /**
1373 * Close the active SMTP session if one exists.
1374 * @return void
1375 */
1376 public function smtpClose()
1377 {
1378 if ($this->smtp !== null) {
1379 if ($this->smtp->connected()) {
1380 $this->smtp->quit();
1381 $this->smtp->close();
1382 }
1383 }
1384 }
1385
1386 /**
1387 * Set the language for error messages.
1388 * Returns false if it cannot load the language file.
1389 * The default language is English.
1390 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1391 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1392 * @return boolean
1393 * @access public
1394 */
1395 public function setLanguage($langcode = 'en', $lang_path = '')
1396 {
1397 // Define full set of translatable strings in English
1398 $PHPMAILER_LANG = array(
1399 'authenticate' => 'SMTP Error: Could not authenticate.',
1400 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1401 'data_not_accepted' => 'SMTP Error: data not accepted.',
1402 'empty_message' => 'Message body empty',
1403 'encoding' => 'Unknown encoding: ',
1404 'execute' => 'Could not execute: ',
1405 'file_access' => 'Could not access file: ',
1406 'file_open' => 'File Error: Could not open file: ',
1407 'from_failed' => 'The following From address failed: ',
1408 'instantiate' => 'Could not instantiate mail function.',
1409 'invalid_address' => 'Invalid address',
1410 'mailer_not_supported' => ' mailer is not supported.',
1411 'provide_address' => 'You must provide at least one recipient email address.',
1412 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1413 'signing' => 'Signing Error: ',
1414 'smtp_connect_failed' => 'SMTP connect() failed.',
1415 'smtp_error' => 'SMTP server error: ',
1416 'variable_set' => 'Cannot set or reset variable: '
1417 );
1418 if (empty($lang_path)) {
1419 // Calculate an absolute path so it can work if CWD is not here
1420 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1421 }
1422 $foundlang = true;
1423 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1424 if ($langcode != 'en') { // There is no English translation file
1425 // Make sure language file path is readable
1426 if (!is_readable($lang_file)) {
1427 $foundlang = false;
1428 } else {
1429 // Overwrite language-specific strings.
1430 // This way we'll never have missing translations.
1431 $foundlang = include $lang_file;
1432 }
1433 }
1434 $this->language = $PHPMAILER_LANG;
1435 return (boolean)$foundlang; // Returns false if language not found
1436 }
1437
1438 /**
1439 * Get the array of strings for the current language.
1440 * @return array
1441 */
1442 public function getTranslations()
1443 {
1444 return $this->language;
1445 }
1446
1447 /**
1448 * Create recipient headers.
1449 * @access public
1450 * @param string $type
1451 * @param array $addr An array of recipient,
1452 * where each recipient is a 2-element indexed array with element 0 containing an address
1453 * and element 1 containing a name, like:
1454 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1455 * @return string
1456 */
1457 public function addrAppend($type, $addr)
1458 {
1459 $addresses = array();
1460 foreach ($addr as $address) {
1461 $addresses[] = $this->addrFormat($address);
1462 }
1463 return $type . ': ' . implode(', ', $addresses) . $this->LE;
1464 }
1465
1466 /**
1467 * Format an address for use in a message header.
1468 * @access public
1469 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1470 * like array('joe@example.com', 'Joe User')
1471 * @return string
1472 */
1473 public function addrFormat($addr)
1474 {
1475 if (empty($addr[1])) { // No name provided
1476 return $this->secureHeader($addr[0]);
1477 } else {
1478 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1479 $addr[0]
1480 ) . '>';
1481 }
1482 }
1483
1484 /**
1485 * Word-wrap message.
1486 * For use with mailers that do not automatically perform wrapping
1487 * and for quoted-printable encoded messages.
1488 * Original written by philippe.
1489 * @param string $message The message to wrap
1490 * @param integer $length The line length to wrap to
1491 * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1492 * @access public
1493 * @return string
1494 */
1495 public function wrapText($message, $length, $qp_mode = false)
1496 {
1497 $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
1498 // If utf-8 encoding is used, we will need to make sure we don't
1499 // split multibyte characters when we wrap
1500 $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1501 $lelen = strlen($this->LE);
1502 $crlflen = strlen(self::CRLF);
1503
1504 $message = $this->fixEOL($message);
1505 if (substr($message, -$lelen) == $this->LE) {
1506 $message = substr($message, 0, -$lelen);
1507 }
1508
1509 $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
1510 $message = '';
1511 for ($i = 0; $i < count($line); $i++) {
1512 $line_part = explode(' ', $line[$i]);
1513 $buf = '';
1514 for ($e = 0; $e < count($line_part); $e++) {
1515 $word = $line_part[$e];
1516 if ($qp_mode and (strlen($word) > $length)) {
1517 $space_left = $length - strlen($buf) - $crlflen;
1518 if ($e != 0) {
1519 if ($space_left > 20) {
1520 $len = $space_left;
1521 if ($is_utf8) {
1522 $len = $this->utf8CharBoundary($word, $len);
1523 } elseif (substr($word, $len - 1, 1) == '=') {
1524 $len--;
1525 } elseif (substr($word, $len - 2, 1) == '=') {
1526 $len -= 2;
1527 }
1528 $part = substr($word, 0, $len);
1529 $word = substr($word, $len);
1530 $buf .= ' ' . $part;
1531 $message .= $buf . sprintf('=%s', self::CRLF);
1532 } else {
1533 $message .= $buf . $soft_break;
1534 }
1535 $buf = '';
1536 }
1537 while (strlen($word) > 0) {
1538 if ($length <= 0) {
1539 break;
1540 }
1541 $len = $length;
1542 if ($is_utf8) {
1543 $len = $this->utf8CharBoundary($word, $len);
1544 } elseif (substr($word, $len - 1, 1) == '=') {
1545 $len--;
1546 } elseif (substr($word, $len - 2, 1) == '=') {
1547 $len -= 2;
1548 }
1549 $part = substr($word, 0, $len);
1550 $word = substr($word, $len);
1551
1552 if (strlen($word) > 0) {
1553 $message .= $part . sprintf('=%s', self::CRLF);
1554 } else {
1555 $buf = $part;
1556 }
1557 }
1558 } else {
1559 $buf_o = $buf;
1560 $buf .= ($e == 0) ? $word : (' ' . $word);
1561
1562 if (strlen($buf) > $length and $buf_o != '') {
1563 $message .= $buf_o . $soft_break;
1564 $buf = $word;
1565 }
1566 }
1567 }
1568 $message .= $buf . self::CRLF;
1569 }
1570
1571 return $message;
1572 }
1573
1574 /**
1575 * Find the last character boundary prior to $maxLength in a utf-8
1576 * quoted (printable) encoded string.
1577 * Original written by Colin Brown.
1578 * @access public
1579 * @param string $encodedText utf-8 QP text
1580 * @param integer $maxLength find last character boundary prior to this length
1581 * @return integer
1582 */
1583 public function utf8CharBoundary($encodedText, $maxLength)
1584 {
1585 $foundSplitPos = false;
1586 $lookBack = 3;
1587 while (!$foundSplitPos) {
1588 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1589 $encodedCharPos = strpos($lastChunk, '=');
1590 if (false !== $encodedCharPos) {
1591 // Found start of encoded character byte within $lookBack block.
1592 // Check the encoded byte value (the 2 chars after the '=')
1593 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1594 $dec = hexdec($hex);
1595 if ($dec < 128) { // Single byte character.
1596 // If the encoded char was found at pos 0, it will fit
1597 // otherwise reduce maxLength to start of the encoded char
1598 $maxLength = ($encodedCharPos == 0) ? $maxLength :
1599 $maxLength - ($lookBack - $encodedCharPos);
1600 $foundSplitPos = true;
1601 } elseif ($dec >= 192) { // First byte of a multi byte character
1602 // Reduce maxLength to split at start of character
1603 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1604 $foundSplitPos = true;
1605 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1606 $lookBack += 3;
1607 }
1608 } else {
1609 // No encoded character found
1610 $foundSplitPos = true;
1611 }
1612 }
1613 return $maxLength;
1614 }
1615
1616 /**
1617 * Set the body wrapping.
1618 * @access public
1619 * @return void
1620 */
1621 public function setWordWrap()
1622 {
1623 if ($this->WordWrap < 1) {
1624 return;
1625 }
1626
1627 switch ($this->message_type) {
1628 case 'alt':
1629 case 'alt_inline':
1630 case 'alt_attach':
1631 case 'alt_inline_attach':
1632 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1633 break;
1634 default:
1635 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1636 break;
1637 }
1638 }
1639
1640 /**
1641 * Assemble message headers.
1642 * @access public
1643 * @return string The assembled headers
1644 */
1645 public function createHeader()
1646 {
1647 $result = '';
1648
1649 // Set the boundaries
1650 $uniq_id = md5(uniqid(time()));
1651 $this->boundary[1] = 'b1_' . $uniq_id;
1652 $this->boundary[2] = 'b2_' . $uniq_id;
1653 $this->boundary[3] = 'b3_' . $uniq_id;
1654
1655 if ($this->MessageDate == '') {
1656 $this->MessageDate = self::rfcDate();
1657 }
1658 $result .= $this->headerLine('Date', $this->MessageDate);
1659
1660
1661 // To be created automatically by mail()
1662 if ($this->SingleTo) {
1663 if ($this->Mailer != 'mail') {
1664 foreach ($this->to as $toaddr) {
1665 $this->SingleToArray[] = $this->addrFormat($toaddr);
1666 }
1667 }
1668 } else {
1669 if (count($this->to) > 0) {
1670 if ($this->Mailer != 'mail') {
1671 $result .= $this->addrAppend('To', $this->to);
1672 }
1673 } elseif (count($this->cc) == 0) {
1674 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1675 }
1676 }
1677
1678 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1679
1680 // sendmail and mail() extract Cc from the header before sending
1681 if (count($this->cc) > 0) {
1682 $result .= $this->addrAppend('Cc', $this->cc);
1683 }
1684
1685 // sendmail and mail() extract Bcc from the header before sending
1686 if ((
1687 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1688 )
1689 and count($this->bcc) > 0
1690 ) {
1691 $result .= $this->addrAppend('Bcc', $this->bcc);
1692 }
1693
1694 if (count($this->ReplyTo) > 0) {
1695 $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1696 }
1697
1698 // mail() sets the subject itself
1699 if ($this->Mailer != 'mail') {
1700 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1701 }
1702
1703 if ($this->MessageID != '') {
1704 $this->lastMessageID = $this->MessageID;
1705 } else {
1706 $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
1707 }
1708 $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
1709 $result .= $this->headerLine('X-Priority', $this->Priority);
1710 if ($this->XMailer == '') {
1711 $result .= $this->headerLine(
1712 'X-Mailer',
1713 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
1714 );
1715 } else {
1716 $myXmailer = trim($this->XMailer);
1717 if ($myXmailer) {
1718 $result .= $this->headerLine('X-Mailer', $myXmailer);
1719 }
1720 }
1721
1722 if ($this->ConfirmReadingTo != '') {
1723 $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1724 }
1725
1726 // Add custom headers
1727 for ($index = 0; $index < count($this->CustomHeader); $index++) {
1728 $result .= $this->headerLine(
1729 trim($this->CustomHeader[$index][0]),
1730 $this->encodeHeader(trim($this->CustomHeader[$index][1]))
1731 );
1732 }
1733 if (!$this->sign_key_file) {
1734 $result .= $this->headerLine('MIME-Version', '1.0');
1735 $result .= $this->getMailMIME();
1736 }
1737
1738 return $result;
1739 }
1740
1741 /**
1742 * Get the message MIME type headers.
1743 * @access public
1744 * @return string
1745 */
1746 public function getMailMIME()
1747 {
1748 $result = '';
1749 $ismultipart = true;
1750 switch ($this->message_type) {
1751 case 'inline':
1752 $result .= $this->headerLine('Content-Type', 'multipart/related;');
1753 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1754 break;
1755 case 'attach':
1756 case 'inline_attach':
1757 case 'alt_attach':
1758 case 'alt_inline_attach':
1759 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1760 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1761 break;
1762 case 'alt':
1763 case 'alt_inline':
1764 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1765 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1766 break;
1767 default:
1768 // Catches case 'plain': and case '':
1769 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
1770 $ismultipart = false;
1771 break;
1772 }
1773 // RFC1341 part 5 says 7bit is assumed if not specified
1774 if ($this->Encoding != '7bit') {
1775 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
1776 if ($ismultipart) {
1777 if ($this->Encoding == '8bit') {
1778 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
1779 }
1780 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
1781 } else {
1782 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1783 }
1784 }
1785
1786 if ($this->Mailer != 'mail') {
1787 $result .= $this->LE;
1788 }
1789
1790 return $result;
1791 }
1792
1793 /**
1794 * Returns the whole MIME message.
1795 * Includes complete headers and body.
1796 * Only valid post preSend().
1797 * @see PHPMailer::preSend()
1798 * @access public
1799 * @return string
1800 */
1801 public function getSentMIMEMessage()
1802 {
1803 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1804 }
1805
1806
1807 /**
1808 * Assemble the message body.
1809 * Returns an empty string on failure.
1810 * @access public
1811 * @throws phpmailerException
1812 * @return string The assembled message body
1813 */
1814 public function createBody()
1815 {
1816 $body = '';
1817
1818 if ($this->sign_key_file) {
1819 $body .= $this->getMailMIME() . $this->LE;
1820 }
1821
1822 $this->setWordWrap();
1823
1824 $bodyEncoding = $this->Encoding;
1825 $bodyCharSet = $this->CharSet;
1826 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
1827 $bodyEncoding = '7bit';
1828 $bodyCharSet = 'us-ascii';
1829 }
1830 $altBodyEncoding = $this->Encoding;
1831 $altBodyCharSet = $this->CharSet;
1832 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
1833 $altBodyEncoding = '7bit';
1834 $altBodyCharSet = 'us-ascii';
1835 }
1836 switch ($this->message_type) {
1837 case 'inline':
1838 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1839 $body .= $this->encodeString($this->Body, $bodyEncoding);
1840 $body .= $this->LE . $this->LE;
1841 $body .= $this->attachAll('inline', $this->boundary[1]);
1842 break;
1843 case 'attach':
1844 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1845 $body .= $this->encodeString($this->Body, $bodyEncoding);
1846 $body .= $this->LE . $this->LE;
1847 $body .= $this->attachAll('attachment', $this->boundary[1]);
1848 break;
1849 case 'inline_attach':
1850 $body .= $this->textLine('--' . $this->boundary[1]);
1851 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1852 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1853 $body .= $this->LE;
1854 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
1855 $body .= $this->encodeString($this->Body, $bodyEncoding);
1856 $body .= $this->LE . $this->LE;
1857 $body .= $this->attachAll('inline', $this->boundary[2]);
1858 $body .= $this->LE;
1859 $body .= $this->attachAll('attachment', $this->boundary[1]);
1860 break;
1861 case 'alt':
1862 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1863 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1864 $body .= $this->LE . $this->LE;
1865 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
1866 $body .= $this->encodeString($this->Body, $bodyEncoding);
1867 $body .= $this->LE . $this->LE;
1868 if (!empty($this->Ical)) {
1869 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1870 $body .= $this->encodeString($this->Ical, $this->Encoding);
1871 $body .= $this->LE . $this->LE;
1872 }
1873 $body .= $this->endBoundary($this->boundary[1]);
1874 break;
1875 case 'alt_inline':
1876 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1877 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1878 $body .= $this->LE . $this->LE;
1879 $body .= $this->textLine('--' . $this->boundary[1]);
1880 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1881 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1882 $body .= $this->LE;
1883 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1884 $body .= $this->encodeString($this->Body, $bodyEncoding);
1885 $body .= $this->LE . $this->LE;
1886 $body .= $this->attachAll('inline', $this->boundary[2]);
1887 $body .= $this->LE;
1888 $body .= $this->endBoundary($this->boundary[1]);
1889 break;
1890 case 'alt_attach':
1891 $body .= $this->textLine('--' . $this->boundary[1]);
1892 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1893 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1894 $body .= $this->LE;
1895 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1896 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1897 $body .= $this->LE . $this->LE;
1898 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1899 $body .= $this->encodeString($this->Body, $bodyEncoding);
1900 $body .= $this->LE . $this->LE;
1901 $body .= $this->endBoundary($this->boundary[2]);
1902 $body .= $this->LE;
1903 $body .= $this->attachAll('attachment', $this->boundary[1]);
1904 break;
1905 case 'alt_inline_attach':
1906 $body .= $this->textLine('--' . $this->boundary[1]);
1907 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1908 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1909 $body .= $this->LE;
1910 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1911 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1912 $body .= $this->LE . $this->LE;
1913 $body .= $this->textLine('--' . $this->boundary[2]);
1914 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1915 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
1916 $body .= $this->LE;
1917 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
1918 $body .= $this->encodeString($this->Body, $bodyEncoding);
1919 $body .= $this->LE . $this->LE;
1920 $body .= $this->attachAll('inline', $this->boundary[3]);
1921 $body .= $this->LE;
1922 $body .= $this->endBoundary($this->boundary[2]);
1923 $body .= $this->LE;
1924 $body .= $this->attachAll('attachment', $this->boundary[1]);
1925 break;
1926 default:
1927 // catch case 'plain' and case ''
1928 $body .= $this->encodeString($this->Body, $bodyEncoding);
1929 break;
1930 }
1931
1932 if ($this->isError()) {
1933 $body = '';
1934 } elseif ($this->sign_key_file) {
1935 try {
1936 if (!defined('PKCS7_TEXT')) {
1937 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
1938 }
1939 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
1940 $file = tempnam(sys_get_temp_dir(), 'mail');
1941 if (false === file_put_contents($file, $body)) {
1942 throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
1943 }
1944 $signed = tempnam(sys_get_temp_dir(), 'signed');
1945 if (@openssl_pkcs7_sign(
1946 $file,
1947 $signed,
1948 'file://' . realpath($this->sign_cert_file),
1949 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
1950 null
1951 )
1952 ) {
1953 @unlink($file);
1954 $body = file_get_contents($signed);
1955 @unlink($signed);
1956 } else {
1957 @unlink($file);
1958 @unlink($signed);
1959 throw new phpmailerException($this->lang('signing') . openssl_error_string());
1960 }
1961 } catch (phpmailerException $exc) {
1962 $body = '';
1963 if ($this->exceptions) {
1964 throw $exc;
1965 }
1966 }
1967 }
1968 return $body;
1969 }
1970
1971 /**
1972 * Return the start of a message boundary.
1973 * @access protected
1974 * @param string $boundary
1975 * @param string $charSet
1976 * @param string $contentType
1977 * @param string $encoding
1978 * @return string
1979 */
1980 protected function getBoundary($boundary, $charSet, $contentType, $encoding)
1981 {
1982 $result = '';
1983 if ($charSet == '') {
1984 $charSet = $this->CharSet;
1985 }
1986 if ($contentType == '') {
1987 $contentType = $this->ContentType;
1988 }
1989 if ($encoding == '') {
1990 $encoding = $this->Encoding;
1991 }
1992 $result .= $this->textLine('--' . $boundary);
1993 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
1994 $result .= $this->LE;
1995 // RFC1341 part 5 says 7bit is assumed if not specified
1996 if ($encoding != '7bit') {
1997 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
1998 }
1999 $result .= $this->LE;
2000
2001 return $result;
2002 }
2003
2004 /**
2005 * Return the end of a message boundary.
2006 * @access protected
2007 * @param string $boundary
2008 * @return string
2009 */
2010 protected function endBoundary($boundary)
2011 {
2012 return $this->LE . '--' . $boundary . '--' . $this->LE;
2013 }
2014
2015 /**
2016 * Set the message type.
2017 * PHPMailer only supports some preset message types,
2018 * not arbitrary MIME structures.
2019 * @access protected
2020 * @return void
2021 */
2022 protected function setMessageType()
2023 {
2024 $type = array();
2025 if ($this->alternativeExists()) {
2026 $type[] = 'alt';
2027 }
2028 if ($this->inlineImageExists()) {
2029 $type[] = 'inline';
2030 }
2031 if ($this->attachmentExists()) {
2032 $type[] = 'attach';
2033 }
2034 $this->message_type = implode('_', $type);
2035 if ($this->message_type == '') {
2036 $this->message_type = 'plain';
2037 }
2038 }
2039
2040 /**
2041 * Format a header line.
2042 * @access public
2043 * @param string $name
2044 * @param string $value
2045 * @return string
2046 */
2047 public function headerLine($name, $value)
2048 {
2049 return $name . ': ' . $value . $this->LE;
2050 }
2051
2052 /**
2053 * Return a formatted mail line.
2054 * @access public
2055 * @param string $value
2056 * @return string
2057 */
2058 public function textLine($value)
2059 {
2060 return $value . $this->LE;
2061 }
2062
2063 /**
2064 * Add an attachment from a path on the filesystem.
2065 * Returns false if the file could not be found or read.
2066 * @param string $path Path to the attachment.
2067 * @param string $name Overrides the attachment name.
2068 * @param string $encoding File encoding (see $Encoding).
2069 * @param string $type File extension (MIME) type.
2070 * @param string $disposition Disposition to use
2071 * @throws phpmailerException
2072 * @return boolean
2073 */
2074 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2075 {
2076 try {
2077 if (!@is_file($path)) {
2078 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2079 }
2080
2081 // If a MIME type is not specified, try to work it out from the file name
2082 if ($type == '') {
2083 $type = self::filenameToType($path);
2084 }
2085
2086 $filename = basename($path);
2087 if ($name == '') {
2088 $name = $filename;
2089 }
2090
2091 $this->attachment[] = array(
2092 0 => $path,
2093 1 => $filename,
2094 2 => $name,
2095 3 => $encoding,
2096 4 => $type,
2097 5 => false, // isStringAttachment
2098 6 => $disposition,
2099 7 => 0
2100 );
2101
2102 } catch (phpmailerException $exc) {
2103 $this->setError($exc->getMessage());
2104 $this->edebug($exc->getMessage());
2105 if ($this->exceptions) {
2106 throw $exc;
2107 }
2108 return false;
2109 }
2110 return true;
2111 }
2112
2113 /**
2114 * Return the array of attachments.
2115 * @return array
2116 */
2117 public function getAttachments()
2118 {
2119 return $this->attachment;
2120 }
2121
2122 /**
2123 * Attach all file, string, and binary attachments to the message.
2124 * Returns an empty string on failure.
2125 * @access protected
2126 * @param string $disposition_type
2127 * @param string $boundary
2128 * @return string
2129 */
2130 protected function attachAll($disposition_type, $boundary)
2131 {
2132 // Return text of body
2133 $mime = array();
2134 $cidUniq = array();
2135 $incl = array();
2136
2137 // Add all attachments
2138 foreach ($this->attachment as $attachment) {
2139 // Check if it is a valid disposition_filter
2140 if ($attachment[6] == $disposition_type) {
2141 // Check for string attachment
2142 $string = '';
2143 $path = '';
2144 $bString = $attachment[5];
2145 if ($bString) {
2146 $string = $attachment[0];
2147 } else {
2148 $path = $attachment[0];
2149 }
2150
2151 $inclhash = md5(serialize($attachment));
2152 if (in_array($inclhash, $incl)) {
2153 continue;
2154 }
2155 $incl[] = $inclhash;
2156 $name = $attachment[2];
2157 $encoding = $attachment[3];
2158 $type = $attachment[4];
2159 $disposition = $attachment[6];
2160 $cid = $attachment[7];
2161 if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2162 continue;
2163 }
2164 $cidUniq[$cid] = true;
2165
2166 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2167 $mime[] = sprintf(
2168 'Content-Type: %s; name="%s"%s',
2169 $type,
2170 $this->encodeHeader($this->secureHeader($name)),
2171 $this->LE
2172 );
2173 // RFC1341 part 5 says 7bit is assumed if not specified
2174 if ($encoding != '7bit') {
2175 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2176 }
2177
2178 if ($disposition == 'inline') {
2179 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2180 }
2181
2182 // If a filename contains any of these chars, it should be quoted,
2183 // but not otherwise: RFC2183 & RFC2045 5.1
2184 // Fixes a warning in IETF's msglint MIME checker
2185 // Allow for bypassing the Content-Disposition header totally
2186 if (!(empty($disposition))) {
2187 $encoded_name = $this->encodeHeader($this->secureHeader($name));
2188 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2189 $mime[] = sprintf(
2190 'Content-Disposition: %s; filename="%s"%s',
2191 $disposition,
2192 $encoded_name,
2193 $this->LE . $this->LE
2194 );
2195 } else {
2196 $mime[] = sprintf(
2197 'Content-Disposition: %s; filename=%s%s',
2198 $disposition,
2199 $encoded_name,
2200 $this->LE . $this->LE
2201 );
2202 }
2203 } else {
2204 $mime[] = $this->LE;
2205 }
2206
2207 // Encode as string attachment
2208 if ($bString) {
2209 $mime[] = $this->encodeString($string, $encoding);
2210 if ($this->isError()) {
2211 return '';
2212 }
2213 $mime[] = $this->LE . $this->LE;
2214 } else {
2215 $mime[] = $this->encodeFile($path, $encoding);
2216 if ($this->isError()) {
2217 return '';
2218 }
2219 $mime[] = $this->LE . $this->LE;
2220 }
2221 }
2222 }
2223
2224 $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2225
2226 return implode('', $mime);
2227 }
2228
2229 /**
2230 * Encode a file attachment in requested format.
2231 * Returns an empty string on failure.
2232 * @param string $path The full path to the file
2233 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2234 * @throws phpmailerException
2235 * @see EncodeFile(encodeFile
2236 * @access protected
2237 * @return string
2238 */
2239 protected function encodeFile($path, $encoding = 'base64')
2240 {
2241 try {
2242 if (!is_readable($path)) {
2243 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2244 }
2245 $magic_quotes = get_magic_quotes_runtime();
2246 if ($magic_quotes) {
2247 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2248 set_magic_quotes_runtime(false);
2249 } else {
2250 //Doesn't exist in PHP 5.4, but we don't need to check because
2251 //get_magic_quotes_runtime always returns false in 5.4+
2252 //so it will never get here
2253 ini_set('magic_quotes_runtime', 0);
2254 }
2255 }
2256 $file_buffer = file_get_contents($path);
2257 $file_buffer = $this->encodeString($file_buffer, $encoding);
2258 if ($magic_quotes) {
2259 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2260 set_magic_quotes_runtime($magic_quotes);
2261 } else {
2262 ini_set('magic_quotes_runtime', ($magic_quotes?'1':'0'));
2263 }
2264 }
2265 return $file_buffer;
2266 } catch (Exception $exc) {
2267 $this->setError($exc->getMessage());
2268 return '';
2269 }
2270 }
2271
2272 /**
2273 * Encode a string in requested format.
2274 * Returns an empty string on failure.
2275 * @param string $str The text to encode
2276 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2277 * @access public
2278 * @return string
2279 */
2280 public function encodeString($str, $encoding = 'base64')
2281 {
2282 $encoded = '';
2283 switch (strtolower($encoding)) {
2284 case 'base64':
2285 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2286 break;
2287 case '7bit':
2288 case '8bit':
2289 $encoded = $this->fixEOL($str);
2290 // Make sure it ends with a line break
2291 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2292 $encoded .= $this->LE;
2293 }
2294 break;
2295 case 'binary':
2296 $encoded = $str;
2297 break;
2298 case 'quoted-printable':
2299 $encoded = $this->encodeQP($str);
2300 break;
2301 default:
2302 $this->setError($this->lang('encoding') . $encoding);
2303 break;
2304 }
2305 return $encoded;
2306 }
2307
2308 /**
2309 * Encode a header string optimally.
2310 * Picks shortest of Q, B, quoted-printable or none.
2311 * @access public
2312 * @param string $str
2313 * @param string $position
2314 * @return string
2315 */
2316 public function encodeHeader($str, $position = 'text')
2317 {
2318 $matchcount = 0;
2319 switch (strtolower($position)) {
2320 case 'phrase':
2321 if (!preg_match('/[\200-\377]/', $str)) {
2322 // Can't use addslashes as we don't know the value of magic_quotes_sybase
2323 $encoded = addcslashes($str, "\0..\37\177\\\"");
2324 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2325 return ($encoded);
2326 } else {
2327 return ("\"$encoded\"");
2328 }
2329 }
2330 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2331 break;
2332 /** @noinspection PhpMissingBreakStatementInspection */
2333 case 'comment':
2334 $matchcount = preg_match_all('/[()"]/', $str, $matches);
2335 // Intentional fall-through
2336 case 'text':
2337 default:
2338 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2339 break;
2340 }
2341
2342 if ($matchcount == 0) { // There are no chars that need encoding
2343 return ($str);
2344 }
2345
2346 $maxlen = 75 - 7 - strlen($this->CharSet);
2347 // Try to select the encoding which should produce the shortest output
2348 if ($matchcount > strlen($str) / 3) {
2349 // More than a third of the content will need encoding, so B encoding will be most efficient
2350 $encoding = 'B';
2351 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2352 // Use a custom function which correctly encodes and wraps long
2353 // multibyte strings without breaking lines within a character
2354 $encoded = $this->base64EncodeWrapMB($str, "\n");
2355 } else {
2356 $encoded = base64_encode($str);
2357 $maxlen -= $maxlen % 4;
2358 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2359 }
2360 } else {
2361 $encoding = 'Q';
2362 $encoded = $this->encodeQ($str, $position);
2363 $encoded = $this->wrapText($encoded, $maxlen, true);
2364 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2365 }
2366
2367 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2368 $encoded = trim(str_replace("\n", $this->LE, $encoded));
2369
2370 return $encoded;
2371 }
2372
2373 /**
2374 * Check if a string contains multi-byte characters.
2375 * @access public
2376 * @param string $str multi-byte text to wrap encode
2377 * @return boolean
2378 */
2379 public function hasMultiBytes($str)
2380 {
2381 if (function_exists('mb_strlen')) {
2382 return (strlen($str) > mb_strlen($str, $this->CharSet));
2383 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2384 return false;
2385 }
2386 }
2387
2388 /**
2389 * Does a string contain any 8-bit chars (in any charset)?
2390 * @param string $text
2391 * @return boolean
2392 */
2393 public function has8bitChars($text)
2394 {
2395 return (boolean)preg_match('/[\x80-\xFF]/', $text);
2396 }
2397
2398 /**
2399 * Encode and wrap long multibyte strings for mail headers
2400 * without breaking lines within a character.
2401 * Adapted from a function by paravoid
2402 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2403 * @access public
2404 * @param string $str multi-byte text to wrap encode
2405 * @param string $linebreak string to use as linefeed/end-of-line
2406 * @return string
2407 */
2408 public function base64EncodeWrapMB($str, $linebreak = null)
2409 {
2410 $start = '=?' . $this->CharSet . '?B?';
2411 $end = '?=';
2412 $encoded = '';
2413 if ($linebreak === null) {
2414 $linebreak = $this->LE;
2415 }
2416
2417 $mb_length = mb_strlen($str, $this->CharSet);
2418 // Each line must have length <= 75, including $start and $end
2419 $length = 75 - strlen($start) - strlen($end);
2420 // Average multi-byte ratio
2421 $ratio = $mb_length / strlen($str);
2422 // Base64 has a 4:3 ratio
2423 $avgLength = floor($length * $ratio * .75);
2424
2425 for ($i = 0; $i < $mb_length; $i += $offset) {
2426 $lookBack = 0;
2427 do {
2428 $offset = $avgLength - $lookBack;
2429 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2430 $chunk = base64_encode($chunk);
2431 $lookBack++;
2432 } while (strlen($chunk) > $length);
2433 $encoded .= $chunk . $linebreak;
2434 }
2435
2436 // Chomp the last linefeed
2437 $encoded = substr($encoded, 0, -strlen($linebreak));
2438 return $encoded;
2439 }
2440
2441 /**
2442 * Encode a string in quoted-printable format.
2443 * According to RFC2045 section 6.7.
2444 * @access public
2445 * @param string $string The text to encode
2446 * @param integer $line_max Number of chars allowed on a line before wrapping
2447 * @return string
2448 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2449 */
2450 public function encodeQP($string, $line_max = 76)
2451 {
2452 if (function_exists('quoted_printable_encode')) { // Use native function if it's available (>= PHP5.3)
2453 return $this->fixEOL(quoted_printable_encode($string));
2454 }
2455 // Fall back to a pure PHP implementation
2456 $string = str_replace(
2457 array('%20', '%0D%0A.', '%0D%0A', '%'),
2458 array(' ', "\r\n=2E", "\r\n", '='),
2459 rawurlencode($string)
2460 );
2461 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2462 return $this->fixEOL($string);
2463 }
2464
2465 /**
2466 * Backward compatibility wrapper for an old QP encoding function that was removed.
2467 * @see PHPMailer::encodeQP()
2468 * @access public
2469 * @param string $string
2470 * @param integer $line_max
2471 * @param boolean $space_conv
2472 * @return string
2473 * @deprecated Use encodeQP instead.
2474 */
2475 public function encodeQPphp(
2476 $string,
2477 $line_max = 76,
2478 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2479 ) {
2480 return $this->encodeQP($string, $line_max);
2481 }
2482
2483 /**
2484 * Encode a string using Q encoding.
2485 * @link http://tools.ietf.org/html/rfc2047
2486 * @param string $str the text to encode
2487 * @param string $position Where the text is going to be used, see the RFC for what that means
2488 * @access public
2489 * @return string
2490 */
2491 public function encodeQ($str, $position = 'text')
2492 {
2493 // There should not be any EOL in the string
2494 $pattern = '';
2495 $encoded = str_replace(array("\r", "\n"), '', $str);
2496 switch (strtolower($position)) {
2497 case 'phrase':
2498 // RFC 2047 section 5.3
2499 $pattern = '^A-Za-z0-9!*+\/ -';
2500 break;
2501 /** @noinspection PhpMissingBreakStatementInspection */
2502 case 'comment':
2503 // RFC 2047 section 5.2
2504 $pattern = '\(\)"';
2505 // intentional fall-through
2506 // for this reason we build the $pattern without including delimiters and []
2507 case 'text':
2508 default:
2509 // RFC 2047 section 5.1
2510 // Replace every high ascii, control, =, ? and _ characters
2511 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2512 break;
2513 }
2514 $matches = array();
2515 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2516 // If the string contains an '=', make sure it's the first thing we replace
2517 // so as to avoid double-encoding
2518 $eqkey = array_search('=', $matches[0]);
2519 if (false !== $eqkey) {
2520 unset($matches[0][$eqkey]);
2521 array_unshift($matches[0], '=');
2522 }
2523 foreach (array_unique($matches[0]) as $char) {
2524 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2525 }
2526 }
2527 // Replace every spaces to _ (more readable than =20)
2528 return str_replace(' ', '_', $encoded);
2529 }
2530
2531
2532 /**
2533 * Add a string or binary attachment (non-filesystem).
2534 * This method can be used to attach ascii or binary data,
2535 * such as a BLOB record from a database.
2536 * @param string $string String attachment data.
2537 * @param string $filename Name of the attachment.
2538 * @param string $encoding File encoding (see $Encoding).
2539 * @param string $type File extension (MIME) type.
2540 * @param string $disposition Disposition to use
2541 * @return void
2542 */
2543 public function addStringAttachment(
2544 $string,
2545 $filename,
2546 $encoding = 'base64',
2547 $type = '',
2548 $disposition = 'attachment'
2549 ) {
2550 // If a MIME type is not specified, try to work it out from the file name
2551 if ($type == '') {
2552 $type = self::filenameToType($filename);
2553 }
2554 // Append to $attachment array
2555 $this->attachment[] = array(
2556 0 => $string,
2557 1 => $filename,
2558 2 => basename($filename),
2559 3 => $encoding,
2560 4 => $type,
2561 5 => true, // isStringAttachment
2562 6 => $disposition,
2563 7 => 0
2564 );
2565 }
2566
2567 /**
2568 * Add an embedded (inline) attachment from a file.
2569 * This can include images, sounds, and just about any other document type.
2570 * These differ from 'regular' attachments in that they are intended to be
2571 * displayed inline with the message, not just attached for download.
2572 * This is used in HTML messages that embed the images
2573 * the HTML refers to using the $cid value.
2574 * @param string $path Path to the attachment.
2575 * @param string $cid Content ID of the attachment; Use this to reference
2576 * the content when using an embedded image in HTML.
2577 * @param string $name Overrides the attachment name.
2578 * @param string $encoding File encoding (see $Encoding).
2579 * @param string $type File MIME type.
2580 * @param string $disposition Disposition to use
2581 * @return boolean True on successfully adding an attachment
2582 */
2583 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2584 {
2585 if (!@is_file($path)) {
2586 $this->setError($this->lang('file_access') . $path);
2587 return false;
2588 }
2589
2590 // If a MIME type is not specified, try to work it out from the file name
2591 if ($type == '') {
2592 $type = self::filenameToType($path);
2593 }
2594
2595 $filename = basename($path);
2596 if ($name == '') {
2597 $name = $filename;
2598 }
2599
2600 // Append to $attachment array
2601 $this->attachment[] = array(
2602 0 => $path,
2603 1 => $filename,
2604 2 => $name,
2605 3 => $encoding,
2606 4 => $type,
2607 5 => false, // isStringAttachment
2608 6 => $disposition,
2609 7 => $cid
2610 );
2611 return true;
2612 }
2613
2614 /**
2615 * Add an embedded stringified attachment.
2616 * This can include images, sounds, and just about any other document type.
2617 * Be sure to set the $type to an image type for images:
2618 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2619 * @param string $string The attachment binary data.
2620 * @param string $cid Content ID of the attachment; Use this to reference
2621 * the content when using an embedded image in HTML.
2622 * @param string $name
2623 * @param string $encoding File encoding (see $Encoding).
2624 * @param string $type MIME type.
2625 * @param string $disposition Disposition to use
2626 * @return boolean True on successfully adding an attachment
2627 */
2628 public function addStringEmbeddedImage(
2629 $string,
2630 $cid,
2631 $name = '',
2632 $encoding = 'base64',
2633 $type = '',
2634 $disposition = 'inline'
2635 ) {
2636 // If a MIME type is not specified, try to work it out from the name
2637 if ($type == '') {
2638 $type = self::filenameToType($name);
2639 }
2640
2641 // Append to $attachment array
2642 $this->attachment[] = array(
2643 0 => $string,
2644 1 => $name,
2645 2 => $name,
2646 3 => $encoding,
2647 4 => $type,
2648 5 => true, // isStringAttachment
2649 6 => $disposition,
2650 7 => $cid
2651 );
2652 return true;
2653 }
2654
2655 /**
2656 * Check if an inline attachment is present.
2657 * @access public
2658 * @return boolean
2659 */
2660 public function inlineImageExists()
2661 {
2662 foreach ($this->attachment as $attachment) {
2663 if ($attachment[6] == 'inline') {
2664 return true;
2665 }
2666 }
2667 return false;
2668 }
2669
2670 /**
2671 * Check if an attachment (non-inline) is present.
2672 * @return boolean
2673 */
2674 public function attachmentExists()
2675 {
2676 foreach ($this->attachment as $attachment) {
2677 if ($attachment[6] == 'attachment') {
2678 return true;
2679 }
2680 }
2681 return false;
2682 }
2683
2684 /**
2685 * Check if this message has an alternative body set.
2686 * @return boolean
2687 */
2688 public function alternativeExists()
2689 {
2690 return !empty($this->AltBody);
2691 }
2692
2693 /**
2694 * Clear all To recipients.
2695 * @return void
2696 */
2697 public function clearAddresses()
2698 {
2699 foreach ($this->to as $to) {
2700 unset($this->all_recipients[strtolower($to[0])]);
2701 }
2702 $this->to = array();
2703 }
2704
2705 /**
2706 * Clear all CC recipients.
2707 * @return void
2708 */
2709 public function clearCCs()
2710 {
2711 foreach ($this->cc as $cc) {
2712 unset($this->all_recipients[strtolower($cc[0])]);
2713 }
2714 $this->cc = array();
2715 }
2716
2717 /**
2718 * Clear all BCC recipients.
2719 * @return void
2720 */
2721 public function clearBCCs()
2722 {
2723 foreach ($this->bcc as $bcc) {
2724 unset($this->all_recipients[strtolower($bcc[0])]);
2725 }
2726 $this->bcc = array();
2727 }
2728
2729 /**
2730 * Clear all ReplyTo recipients.
2731 * @return void
2732 */
2733 public function clearReplyTos()
2734 {
2735 $this->ReplyTo = array();
2736 }
2737
2738 /**
2739 * Clear all recipient types.
2740 * @return void
2741 */
2742 public function clearAllRecipients()
2743 {
2744 $this->to = array();
2745 $this->cc = array();
2746 $this->bcc = array();
2747 $this->all_recipients = array();
2748 }
2749
2750 /**
2751 * Clear all filesystem, string, and binary attachments.
2752 * @return void
2753 */
2754 public function clearAttachments()
2755 {
2756 $this->attachment = array();
2757 }
2758
2759 /**
2760 * Clear all custom headers.
2761 * @return void
2762 */
2763 public function clearCustomHeaders()
2764 {
2765 $this->CustomHeader = array();
2766 }
2767
2768 /**
2769 * Add an error message to the error container.
2770 * @access protected
2771 * @param string $msg
2772 * @return void
2773 */
2774 protected function setError($msg)
2775 {
2776 $this->error_count++;
2777 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2778 $lasterror = $this->smtp->getError();
2779 if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2780 $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
2781 }
2782 }
2783 $this->ErrorInfo = $msg;
2784 }
2785
2786 /**
2787 * Return an RFC 822 formatted date.
2788 * @access public
2789 * @return string
2790 * @static
2791 */
2792 public static function rfcDate()
2793 {
2794 // Set the time zone to whatever the default is to avoid 500 errors
2795 // Will default to UTC if it's not set properly in php.ini
2796 date_default_timezone_set(@date_default_timezone_get());
2797 return date('D, j M Y H:i:s O');
2798 }
2799
2800 /**
2801 * Get the server hostname.
2802 * Returns 'localhost.localdomain' if unknown.
2803 * @access protected
2804 * @return string
2805 */
2806 protected function serverHostname()
2807 {
2808 $result = 'localhost.localdomain';
2809 if (!empty($this->Hostname)) {
2810 $result = $this->Hostname;
2811 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
2812 $result = $_SERVER['SERVER_NAME'];
2813 } elseif (function_exists('gethostname') && gethostname() !== false) {
2814 $result = gethostname();
2815 } elseif (php_uname('n') !== false) {
2816 $result = php_uname('n');
2817 }
2818 return $result;
2819 }
2820
2821 /**
2822 * Get an error message in the current language.
2823 * @access protected
2824 * @param string $key
2825 * @return string
2826 */
2827 protected function lang($key)
2828 {
2829 if (count($this->language) < 1) {
2830 $this->setLanguage('en'); // set the default language
2831 }
2832
2833 if (isset($this->language[$key])) {
2834 return $this->language[$key];
2835 } else {
2836 return 'Language string failed to load: ' . $key;
2837 }
2838 }
2839
2840 /**
2841 * Check if an error occurred.
2842 * @access public
2843 * @return boolean True if an error did occur.
2844 */
2845 public function isError()
2846 {
2847 return ($this->error_count > 0);
2848 }
2849
2850 /**
2851 * Ensure consistent line endings in a string.
2852 * Changes every end of line from CRLF, CR or LF to $this->LE.
2853 * @access public
2854 * @param string $str String to fixEOL
2855 * @return string
2856 */
2857 public function fixEOL($str)
2858 {
2859 // Normalise to \n
2860 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
2861 // Now convert LE as needed
2862 if ($this->LE !== "\n") {
2863 $nstr = str_replace("\n", $this->LE, $nstr);
2864 }
2865 return $nstr;
2866 }
2867
2868 /**
2869 * Add a custom header.
2870 * $name value can be overloaded to contain
2871 * both header name and value (name:value)
2872 * @access public
2873 * @param string $name Custom header name
2874 * @param string $value Header value
2875 * @return void
2876 */
2877 public function addCustomHeader($name, $value = null)
2878 {
2879 if ($value === null) {
2880 // Value passed in as name:value
2881 $this->CustomHeader[] = explode(':', $name, 2);
2882 } else {
2883 $this->CustomHeader[] = array($name, $value);
2884 }
2885 }
2886
2887 /**
2888 * Create a message from an HTML string.
2889 * Automatically makes modifications for inline images and backgrounds
2890 * and creates a plain-text version by converting the HTML.
2891 * Overwrites any existing values in $this->Body and $this->AltBody
2892 * @access public
2893 * @param string $message HTML message string
2894 * @param string $basedir baseline directory for path
2895 * @param boolean|callable $advanced Whether to use the internal HTML to text converter
2896 * or your own custom converter @see html2text()
2897 * @return string $message
2898 */
2899 public function msgHTML($message, $basedir = '', $advanced = false)
2900 {
2901 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
2902 if (isset($images[2])) {
2903 foreach ($images[2] as $imgindex => $url) {
2904 // Convert data URIs into embedded images
2905 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
2906 $data = substr($url, strpos($url, ','));
2907 if ($match[2]) {
2908 $data = base64_decode($data);
2909 } else {
2910 $data = rawurldecode($data);
2911 }
2912 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2913 if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) {
2914 $message = str_replace(
2915 $images[0][$imgindex],
2916 $images[1][$imgindex] . '="cid:' . $cid . '"',
2917 $message
2918 );
2919 }
2920 } elseif (!preg_match('#^[A-z]+://#', $url)) {
2921 // Do not change urls for absolute images (thanks to corvuscorax)
2922 $filename = basename($url);
2923 $directory = dirname($url);
2924 if ($directory == '.') {
2925 $directory = '';
2926 }
2927 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2928 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2929 $basedir .= '/';
2930 }
2931 if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2932 $directory .= '/';
2933 }
2934 if ($this->addEmbeddedImage(
2935 $basedir . $directory . $filename,
2936 $cid,
2937 $filename,
2938 'base64',
2939 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
2940 )
2941 ) {
2942 $message = preg_replace(
2943 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
2944 $images[1][$imgindex] . '="cid:' . $cid . '"',
2945 $message
2946 );
2947 }
2948 }
2949 }
2950 }
2951 $this->isHTML(true);
2952 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2953 $this->Body = $this->normalizeBreaks($message);
2954 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
2955 if (empty($this->AltBody)) {
2956 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
2957 self::CRLF . self::CRLF;
2958 }
2959 return $this->Body;
2960 }
2961
2962 /**
2963 * Convert an HTML string into plain text.
2964 * This is used by msgHTML().
2965 * Note - older versions of this function used a bundled advanced converter
2966 * which was been removed for license reasons in #232
2967 * Example usage:
2968 * <code>
2969 * // Use default conversion
2970 * $plain = $mail->html2text($html);
2971 * // Use your own custom converter
2972 * $plain = $mail->html2text($html, function($html) {
2973 * $converter = new MyHtml2text($html);
2974 * return $converter->get_text();
2975 * });
2976 * </code>
2977 * @param string $html The HTML text to convert
2978 * @param boolean|callable $advanced Any boolean value to use the internal converter,
2979 * or provide your own callable for custom conversion.
2980 * @return string
2981 */
2982 public function html2text($html, $advanced = false)
2983 {
2984 if (is_callable($advanced)) {
2985 return call_user_func($advanced, $html);
2986 }
2987 return html_entity_decode(
2988 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
2989 ENT_QUOTES,
2990 $this->CharSet
2991 );
2992 }
2993
2994 /**
2995 * Get the MIME type for a file extension.
2996 * @param string $ext File extension
2997 * @access public
2998 * @return string MIME type of file.
2999 * @static
3000 */
3001 public static function _mime_types($ext = '')
3002 {
3003 $mimes = array(
3004 'xl' => 'application/excel',
3005 'js' => 'application/javascript',
3006 'hqx' => 'application/mac-binhex40',
3007 'cpt' => 'application/mac-compactpro',
3008 'bin' => 'application/macbinary',
3009 'doc' => 'application/msword',
3010 'word' => 'application/msword',
3011 'class' => 'application/octet-stream',
3012 'dll' => 'application/octet-stream',
3013 'dms' => 'application/octet-stream',
3014 'exe' => 'application/octet-stream',
3015 'lha' => 'application/octet-stream',
3016 'lzh' => 'application/octet-stream',
3017 'psd' => 'application/octet-stream',
3018 'sea' => 'application/octet-stream',
3019 'so' => 'application/octet-stream',
3020 'oda' => 'application/oda',
3021 'pdf' => 'application/pdf',
3022 'ai' => 'application/postscript',
3023 'eps' => 'application/postscript',
3024 'ps' => 'application/postscript',
3025 'smi' => 'application/smil',
3026 'smil' => 'application/smil',
3027 'mif' => 'application/vnd.mif',
3028 'xls' => 'application/vnd.ms-excel',
3029 'ppt' => 'application/vnd.ms-powerpoint',
3030 'wbxml' => 'application/vnd.wap.wbxml',
3031 'wmlc' => 'application/vnd.wap.wmlc',
3032 'dcr' => 'application/x-director',
3033 'dir' => 'application/x-director',
3034 'dxr' => 'application/x-director',
3035 'dvi' => 'application/x-dvi',
3036 'gtar' => 'application/x-gtar',
3037 'php3' => 'application/x-httpd-php',
3038 'php4' => 'application/x-httpd-php',
3039 'php' => 'application/x-httpd-php',
3040 'phtml' => 'application/x-httpd-php',
3041 'phps' => 'application/x-httpd-php-source',
3042 'swf' => 'application/x-shockwave-flash',
3043 'sit' => 'application/x-stuffit',
3044 'tar' => 'application/x-tar',
3045 'tgz' => 'application/x-tar',
3046 'xht' => 'application/xhtml+xml',
3047 'xhtml' => 'application/xhtml+xml',
3048 'zip' => 'application/zip',
3049 'mid' => 'audio/midi',
3050 'midi' => 'audio/midi',
3051 'mp2' => 'audio/mpeg',
3052 'mp3' => 'audio/mpeg',
3053 'mpga' => 'audio/mpeg',
3054 'aif' => 'audio/x-aiff',
3055 'aifc' => 'audio/x-aiff',
3056 'aiff' => 'audio/x-aiff',
3057 'ram' => 'audio/x-pn-realaudio',
3058 'rm' => 'audio/x-pn-realaudio',
3059 'rpm' => 'audio/x-pn-realaudio-plugin',
3060 'ra' => 'audio/x-realaudio',
3061 'wav' => 'audio/x-wav',
3062 'bmp' => 'image/bmp',
3063 'gif' => 'image/gif',
3064 'jpeg' => 'image/jpeg',
3065 'jpe' => 'image/jpeg',
3066 'jpg' => 'image/jpeg',
3067 'png' => 'image/png',
3068 'tiff' => 'image/tiff',
3069 'tif' => 'image/tiff',
3070 'eml' => 'message/rfc822',
3071 'css' => 'text/css',
3072 'html' => 'text/html',
3073 'htm' => 'text/html',
3074 'shtml' => 'text/html',
3075 'log' => 'text/plain',
3076 'text' => 'text/plain',
3077 'txt' => 'text/plain',
3078 'rtx' => 'text/richtext',
3079 'rtf' => 'text/rtf',
3080 'vcf' => 'text/vcard',
3081 'vcard' => 'text/vcard',
3082 'xml' => 'text/xml',
3083 'xsl' => 'text/xml',
3084 'mpeg' => 'video/mpeg',
3085 'mpe' => 'video/mpeg',
3086 'mpg' => 'video/mpeg',
3087 'mov' => 'video/quicktime',
3088 'qt' => 'video/quicktime',
3089 'rv' => 'video/vnd.rn-realvideo',
3090 'avi' => 'video/x-msvideo',
3091 'movie' => 'video/x-sgi-movie'
3092 );
3093 return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
3094 }
3095
3096 /**
3097 * Map a file name to a MIME type.
3098 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3099 * @param string $filename A file name or full path, does not need to exist as a file
3100 * @return string
3101 * @static
3102 */
3103 public static function filenameToType($filename)
3104 {
3105 // In case the path is a URL, strip any query string before getting extension
3106 $qpos = strpos($filename, '?');
3107 if (false !== $qpos) {
3108 $filename = substr($filename, 0, $qpos);
3109 }
3110 $pathinfo = self::mb_pathinfo($filename);
3111 return self::_mime_types($pathinfo['extension']);
3112 }
3113
3114 /**
3115 * Multi-byte-safe pathinfo replacement.
3116 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3117 * Works similarly to the one in PHP >= 5.2.0
3118 * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3119 * @param string $path A filename or path, does not need to exist as a file
3120 * @param integer|string $options Either a PATHINFO_* constant,
3121 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3122 * @return string|array
3123 * @static
3124 */
3125 public static function mb_pathinfo($path, $options = null)
3126 {
3127 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3128 $pathinfo = array();
3129 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3130 if (array_key_exists(1, $pathinfo)) {
3131 $ret['dirname'] = $pathinfo[1];
3132 }
3133 if (array_key_exists(2, $pathinfo)) {
3134 $ret['basename'] = $pathinfo[2];
3135 }
3136 if (array_key_exists(5, $pathinfo)) {
3137 $ret['extension'] = $pathinfo[5];
3138 }
3139 if (array_key_exists(3, $pathinfo)) {
3140 $ret['filename'] = $pathinfo[3];
3141 }
3142 }
3143 switch ($options) {
3144 case PATHINFO_DIRNAME:
3145 case 'dirname':
3146 return $ret['dirname'];
3147 case PATHINFO_BASENAME:
3148 case 'basename':
3149 return $ret['basename'];
3150 case PATHINFO_EXTENSION:
3151 case 'extension':
3152 return $ret['extension'];
3153 case PATHINFO_FILENAME:
3154 case 'filename':
3155 return $ret['filename'];
3156 default:
3157 return $ret;
3158 }
3159 }
3160
3161 /**
3162 * Set or reset instance properties.
3163 * You should avoid this function - it's more verbose, less efficient, more error-prone and
3164 * harder to debug than setting properties directly.
3165 * Usage Example:
3166 * `$mail->set('SMTPSecure', 'tls');`
3167 * is the same as:
3168 * `$mail->SMTPSecure = 'tls';`
3169 * @access public
3170 * @param string $name The property name to set
3171 * @param mixed $value The value to set the property to
3172 * @return boolean
3173 * @TODO Should this not be using the __set() magic function?
3174 */
3175 public function set($name, $value = '')
3176 {
3177 if (property_exists($this, $name)) {
3178 $this->$name = $value;
3179 return true;
3180 } else {
3181 $this->setError($this->lang('variable_set') . $name);
3182 return false;
3183 }
3184 }
3185
3186 /**
3187 * Strip newlines to prevent header injection.
3188 * @access public
3189 * @param string $str
3190 * @return string
3191 */
3192 public function secureHeader($str)
3193 {
3194 return trim(str_replace(array("\r", "\n"), '', $str));
3195 }
3196
3197 /**
3198 * Normalize line breaks in a string.
3199 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3200 * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3201 * @param string $text
3202 * @param string $breaktype What kind of line break to use, defaults to CRLF
3203 * @return string
3204 * @access public
3205 * @static
3206 */
3207 public static function normalizeBreaks($text, $breaktype = "\r\n")
3208 {
3209 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3210 }
3211
3212
3213 /**
3214 * Set the public and private key files and password for S/MIME signing.
3215 * @access public
3216 * @param string $cert_filename
3217 * @param string $key_filename
3218 * @param string $key_pass Password for private key
3219 */
3220 public function sign($cert_filename, $key_filename, $key_pass)
3221 {
3222 $this->sign_cert_file = $cert_filename;
3223 $this->sign_key_file = $key_filename;
3224 $this->sign_key_pass = $key_pass;
3225 }
3226
3227 /**
3228 * Quoted-Printable-encode a DKIM header.
3229 * @access public
3230 * @param string $txt
3231 * @return string
3232 */
3233 public function DKIM_QP($txt)
3234 {
3235 $line = '';
3236 for ($i = 0; $i < strlen($txt); $i++) {
3237 $ord = ord($txt[$i]);
3238 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3239 $line .= $txt[$i];
3240 } else {
3241 $line .= '=' . sprintf('%02X', $ord);
3242 }
3243 }
3244 return $line;
3245 }
3246
3247 /**
3248 * Generate a DKIM signature.
3249 * @access public
3250 * @param string $signHeader
3251 * @throws phpmailerException
3252 * @return string
3253 */
3254 public function DKIM_Sign($signHeader)
3255 {
3256 if (!defined('PKCS7_TEXT')) {
3257 if ($this->exceptions) {
3258 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
3259 }
3260 return '';
3261 }
3262 $privKeyStr = file_get_contents($this->DKIM_private);
3263 if ($this->DKIM_passphrase != '') {
3264 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3265 } else {
3266 $privKey = $privKeyStr;
3267 }
3268 if (openssl_sign($signHeader, $signature, $privKey)) {
3269 return base64_encode($signature);
3270 }
3271 return '';
3272 }
3273
3274 /**
3275 * Generate a DKIM canonicalization header.
3276 * @access public
3277 * @param string $signHeader Header
3278 * @return string
3279 */
3280 public function DKIM_HeaderC($signHeader)
3281 {
3282 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3283 $lines = explode("\r\n", $signHeader);
3284 foreach ($lines as $key => $line) {
3285 list($heading, $value) = explode(':', $line, 2);
3286 $heading = strtolower($heading);
3287 $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
3288 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3289 }
3290 $signHeader = implode("\r\n", $lines);
3291 return $signHeader;
3292 }
3293
3294 /**
3295 * Generate a DKIM canonicalization body.
3296 * @access public
3297 * @param string $body Message Body
3298 * @return string
3299 */
3300 public function DKIM_BodyC($body)
3301 {
3302 if ($body == '') {
3303 return "\r\n";
3304 }
3305 // stabilize line endings
3306 $body = str_replace("\r\n", "\n", $body);
3307 $body = str_replace("\n", "\r\n", $body);
3308 // END stabilize line endings
3309 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3310 $body = substr($body, 0, strlen($body) - 2);
3311 }
3312 return $body;
3313 }
3314
3315 /**
3316 * Create the DKIM header and body in a new message header.
3317 * @access public
3318 * @param string $headers_line Header lines
3319 * @param string $subject Subject
3320 * @param string $body Body
3321 * @return string
3322 */
3323 public function DKIM_Add($headers_line, $subject, $body)
3324 {
3325 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3326 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3327 $DKIMquery = 'dns/txt'; // Query method
3328 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3329 $subject_header = "Subject: $subject";
3330 $headers = explode($this->LE, $headers_line);
3331 $from_header = '';
3332 $to_header = '';
3333 $current = '';
3334 foreach ($headers as $header) {
3335 if (strpos($header, 'From:') === 0) {
3336 $from_header = $header;
3337 $current = 'from_header';
3338 } elseif (strpos($header, 'To:') === 0) {
3339 $to_header = $header;
3340 $current = 'to_header';
3341 } else {
3342 if ($current && strpos($header, ' =?') === 0) {
3343 $current .= $header;
3344 } else {
3345 $current = '';
3346 }
3347 }
3348 }
3349 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3350 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3351 $subject = str_replace(
3352 '|',
3353 '=7C',
3354 $this->DKIM_QP($subject_header)
3355 ); // Copied header fields (dkim-quoted-printable)
3356 $body = $this->DKIM_BodyC($body);
3357 $DKIMlen = strlen($body); // Length of body
3358 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3359 $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
3360 $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3361 $DKIMsignatureType . '; q=' .
3362 $DKIMquery . '; l=' .
3363 $DKIMlen . '; s=' .
3364 $this->DKIM_selector .
3365 ";\r\n" .
3366 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3367 "\th=From:To:Subject;\r\n" .
3368 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3369 "\tz=$from\r\n" .
3370 "\t|$to\r\n" .
3371 "\t|$subject;\r\n" .
3372 "\tbh=" . $DKIMb64 . ";\r\n" .
3373 "\tb=";
3374 $toSign = $this->DKIM_HeaderC(
3375 $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
3376 );
3377 $signed = $this->DKIM_Sign($toSign);
3378 return $dkimhdrs . $signed . "\r\n";
3379 }
3380
3381 /**
3382 * Allows for public read access to 'to' property.
3383 * @access public
3384 * @return array
3385 */
3386 public function getToAddresses()
3387 {
3388 return $this->to;
3389 }
3390
3391 /**
3392 * Allows for public read access to 'cc' property.
3393 * @access public
3394 * @return array
3395 */
3396 public function getCcAddresses()
3397 {
3398 return $this->cc;
3399 }
3400
3401 /**
3402 * Allows for public read access to 'bcc' property.
3403 * @access public
3404 * @return array
3405 */
3406 public function getBccAddresses()
3407 {
3408 return $this->bcc;
3409 }
3410
3411 /**
3412 * Allows for public read access to 'ReplyTo' property.
3413 * @access public
3414 * @return array
3415 */
3416 public function getReplyToAddresses()
3417 {
3418 return $this->ReplyTo;
3419 }
3420
3421 /**
3422 * Allows for public read access to 'all_recipients' property.
3423 * @access public
3424 * @return array
3425 */
3426 public function getAllRecipientAddresses()
3427 {
3428 return $this->all_recipients;
3429 }
3430
3431 /**
3432 * Perform a callback.
3433 * @param boolean $isSent
3434 * @param array $to
3435 * @param array $cc
3436 * @param array $bcc
3437 * @param string $subject
3438 * @param string $body
3439 * @param string $from
3440 */
3441 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3442 {
3443 if (!empty($this->action_function) && is_callable($this->action_function)) {
3444 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3445 call_user_func_array($this->action_function, $params);
3446 }
3447 }
3448 }
3449
3450 /**
3451 * PHPMailer exception handler
3452 * @package PHPMailer
3453 */
3454 class phpmailerException extends Exception
3455 {
3456 /**
3457 * Prettify error message output
3458 * @return string
3459 */
3460 public function errorMessage()
3461 {
3462 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3463 return $errorMsg;
3464 }
3465 }